条款01:视C++为一个语言联邦
将C++视为一个由相关语言组成的联邦,而非单一语言。即视为以下4个次语言组成的联邦:
(1)C:区块、语句、预处理器、内置数据类型、数组、指针等统统来自C。
(2)Obiect-Oriented C++:classes(包括构造函数和析构函数)、封装、继承、多态、虚函数(动态绑定)…等等
(3)Templates C++:泛型编程。
(4)STL:是个templates程序库。
条款02:尽量以const、emum、inline替换#define
请记住:
- 对于单纯常量,最好以const对象或enums替换#define。对于形似函数的宏,最好改用inline函数替换#define。比如这种:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
1、用const 常量 替换#define有2个好处:
(1)当出现编译错误时,方便调试,因为#define会在预处理时进行替换。
(2)对浮点常量而言,使用常量可能比使用#define导致较小量的码。
2、#define不能够用来定义class专属常量,也不能够提供任何封装性,而const成员变量是可以被封装的。
3、一个属于枚举类型的数值,可权充ints被使用。
class GamePlayer { private: enum {NumTurns = 5}; int scores[NumTurns]; ... };
条款03:尽可能使用const
请记住:
- 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性(conceptual constness)。当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
1、const用于指针
如果关键字const出现在星号左边,表示被指物是常量;
如果出现在右边,表示指针自身是常量;
如果出现在*号两边,表示被指物指针两者都是常量。
char greeting[] = "Hello; char* p = greeting; //non-const pointer, non-const data const char * p = greeting; //non-const pointer, const data char* const p = greeting; //const pointer, non-const data const char* const p = greeting; //const pointer, const data
2、const用于迭代器
std::vectorvec; ... const std::vector ::iterator iter = vec.begin(); //iter的作用像个T* const *iter = 10; //没问题,改变iter所指物 ++iter; //错误!iter是const std::vector ::const_iterator cIter = vec.begin(); cIter的作用像个const T* *cIter = 10; //错误!*cIter是const ++cIter; //没问题,改变cIter。
3、令函数返回常量值
const fun();
4、const成员函数
将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。
两个成员函数如果只是常量性不同,可以被重载。
const成员函数不可以更改对象内任何non-static成员变量。
class CTextBlock { public: ... std::size_t length() const; private: char* pText; std::size_t textLength; bool lengthIsValid; } std::size_t CTextBlock::length() const { if(!lengthIsValid) { textLength = std::strlen(pText);//错误!在const成员函数内不能 lengthIsValid = true;//赋值给textLength和lengthIsValid。 } return textLength; }
解决办法:利用C++的一个与const相关的摆动场:mutable(可变的)。mutable释放掉non-static成员变量的bitwise constness约束。
在这里插入代码class CTextBlock { public: ... std::size_t length() const; private: char* pText; mutable std::size_t textLength;//这些成员变量可能总是会被更改,即使在const mutable bool lengthIsValid; //成员函数内 } std::size_t CTextBlock::length() const { if(!lengthIsValid) { textLength = std::strlen(pText);//现在,可以这样, lengthIsValid = true;//也可以这样 } return textLength; }片
5、在const和non-const成员函数中避免重复
比如:
class TextBlock { public: ... const char& operator[] (std::size_t position) const { ... //边界检验 ... //志记数据访问 ... //检验数据完整性 return text[position]; } char& operator[] (std::size_t position) { ... //边界检验 ... //志记数据访问 ... //检验数据完整性 return text[position]; } private: std::string text; };
为了避免代码重复,利用2次转型(第1次:为*this添加const,这使接下来调用openator[ ]时,得以调用const版本。第2次,从const operator[]的返回值中移除const。),将上面代码修改为:
class TextBlock { public: ... const char& operator[] (std::size_t position) const //一如既往 { ... //边界检验 ... //志记数据访问 ... //检验数据完整性 return text[position]; } char& operator[] (std::size_t position) //现在只调用cosnt op[] { return const_cast(static_cast (*this)[position]); } ... };
条款04:确定对象被使用前已先被初始化
请记住:
- 为内置型对象进行手工初始化,因为C++不保证初始化它们。构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值 *** 作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。为免除”跨编译单元之初始化次序“问题,请以local static对象替换non-local static对象。
1、对大多数类型而言,比起先调用default构造函数然后再调用copy assignment *** 作符,单只调用一次copy构造函数是比较高效的,有时甚至高效的多。
2、base classes早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化。
class PhoneNumber { ... }; class ABEntry //ABEntry = "Address Book Entry" { public: ABEntry(const std::string& name, const std::string& address, const std::list& phones); private: std::string theName; //用初始化列表效率比赋值高。 std::string theAddress; std::list thePhones; int numTimesConsulted; //内置类型用成员初始化列表初始化或赋值,效率相同 }; ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list & phones) { theName = name; //这些都是赋值,而非初始化。因为对象的成员变量的初始化动作发生在 theAddress = address;//进入构造函数本体之前。 thePhones = phones; numTimesConsulted = 0; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)