条款03:尽可能使用const
若想在const成员函数中修改成员变量,需要在成员变量前加mutable
class CTextBlock {
std::size_t length() const;
private:
char* pText;
mutable size_t textLength; //这些成员变量可能总是会被修改
mutable bool lengthIsValid; //即使在const成员函数内
};
size_t CTextBlock::length() const
{
if (!lengthIsValid) {
textLength = strlen(pText); //可修改
lengthIsValid = true;
}
return textLength;
}
在const和non-const成员函数中避免重复
- static_cast强制类型转换 static_cast<new_type>
- const_cast <new_type> 移除变量的const
class TextBlock {
const char& operator[](size_t position) const
{
return text[position];
}
char& operator[](size_t position)
{
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
} //两次转型:第一次为*this添加const,第二次从const operator[]的返回值中移除const
private:
string text;
};
不能用const调用non-const
总结:
条款04:确定对象被使用前已先被初始化
1、区分初始化和赋值的区别
2、如果成员变量是const或references,则一定需要初值,不能被赋值
3、如果classes拥有多个构造函数,可以合理地在初值列表中将那些赋值和初始化表现一样好的成员变量移往某个函数(private中),供所有构造函数调用。
static:包括global对象,定义在namespace作用域内的对象,被声明为static的对象。
函数内的static对象:local static
其他static对象:non-local static
static对象在main函数结束后才调用析构函数
C++对“定义于不同的编译单元内的non-local static对象”的初始化相对次序无明确定义。
在一个文件中调用另一个文件的non-local static 无法保证调用前已经被初始化
解决方案:将non-local static搬到自己的专属函数中(在此函数中被声明为static),该函数返回一个reference指向它所含的对象-------Singleton模式
条款06:若不想使用编译器自动生成的函数,就该明确拒绝
方法一:为阻止编译器自动声明copy构造和copy assignment操作符,应将他们声明为private,且不定义(防止member函数和friend函数调用,会在连接期报错)
方法二:继承Uncopyable(可以在member函数和friend函数调用时,将连接期错误移至编译期)
class Uncopyable {
protected:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator = (const Uncopyable&);
};
class HomeForSale :private Uncopyable {
};
条款07:为多态基类声明virtual析构函数
-
如果基类中含virtual函数,则几乎确定应该也有一个virtual析构函数,否则当derived class对象经由一个base class指针删除时,因为base class带着一个non-virtual析构函数,导致derved成分没被销毁,造成资源泄露
-
Classes的设计目的如果不是为了作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数
条款08:别让异常逃离析构函数
防止异常逃离析构函数的方法
- 如果close抛出异常就结束程序,通过调用abort完成
class DBConnection {
public:
static DBConnection create()
{
DBConnection a;
return a;
}
void close();
};
void DBConnection::close() {
}
class DBconn {
public:
DBconn(DBConnection DB):db(DB){}
~DBconn();
private:
DBConnection db;
};
DBconn::~DBconn() {
try { db.close(); }
catch (...) {
std::abort();
}
}
- 较佳策略是重新设计BDConn接口,使客户有机会对可能出现的问题作出反应。
class DBconn {
public:
DBconn(DBConnection DB):db(DB){}
void close() {
db.close();
closed = true;
}
~DBconn() {
if (!closed) {
try { db.close(); }
catch (...) {
std::abort();
}
}
}
private:
DBConnection db;
bool closed;
};
条款09:绝不在构造和析构过程中调用virtual函数
例子 书p48、49
在base class中调用virtual函数,调用的是base class的版本,而非derived class版本,因为在构造期间virtual函数不会下降到derived classes阶层,非正式说法:base class构造期间,virtual函数不是virtual函数
条款10:令operator= 返回一个reference to *this
如
Widget& operator=(const Widget& rhs)
{
…
return *this;
}
条款11:在operator=中处理“自我赋值”
class Widget{……};
Widget w;
…
w = w; //赋值给自己
潜在的自我赋值
a[i]=a[j]; //若i和j具有相同的值
*px = *py //若px和py恰巧指向同一个东西
实际上两个对象只要来自同一个继承体系,甚至不需要声明为相同类型就可能造成“同名
class Base{……};
class Derived:public Base{……};
void doSomething(const Base& rb,Derived* pd); //rb和pd有可能其实是同一个对象
如果自己尝试自行管理资源,可能会掉进“在停止使用资源之前意外释放了它”的陷阱
条款12:复制对象时勿忘其每一个成分
- copying函数应该确保复制“对象内的所有成员变量”,及所有“base class成分”
- 不要尝试以某个copying函数实现另一个copying函数,应将共同机能放进第三个函数中, 并由两个copying函数调用。