05、了解C++默默编写并调用哪些函数
(1)、默认构造函数
Empty(){...} //调用父类构造函数,non-static成员变量构造函数,不会默认初始化内置类型
(2)、析构函数
~Empty(){...} //调用父类析构函数,non-static成员变量析构函数
(3)、拷贝构造函数
Empty(const Empty& rhs){...} //单纯地将来源对象的每一个non-static变量拷贝到目标对象
(4)、赋值构造函数
Empty& operator=(const Empty& rhs){...} //单纯地将来源对象的每一个non-static变量拷贝到目标对象
当类内有引用、const、及父类的拷贝构造函数,赋值构造函数为private时,编译器会拒绝生成这一类函数。换言之,如果类内有引用、const成员变量,或者其父类的相关函数不可访问时,必须手动生成。有指针类型变量时,存在“深拷贝、浅拷贝”问题!!!
class Empty:
{
public:
Empty(){...}
Empty(const Empty& rhs){...}
~Empty(){...}
Empty& operator=(const Empty& rhs){...}
private:
const int m_cInt; //只读成员变量
int &ref; //引用型成员变量,必须在每个构造函数手动初始化
};
06、若不想使用编译器自动生成的函数,就该明确拒绝
禁止被拷贝构造或赋值构造的做法:
(1)、将相应成员函数声明为private且不去实现它。(链接期出错)
class HomeForSale
{
public:
HomeForSale();
~HomeForSale();
private:
HomeForSale(const HomeForSale& rhs); //只有声明
HomeForSale& operator=(const HomeForSale& rhs);
};
(2)、private继承Uncopyable类(编译期出错)
class Uncopyable
{
public:
Uncopyable(){};
~Uncopyable(){};
private:
Uncopyable(const Uncopyable& rhs);
Uncopyable& operator=(const Uncopyable& rhs);
};
07、为多态基类声明virtual析构函数
(1)、polymorphic(带多态性质的)base classes应该声明一个virtual析构函数,这样通过delete 基类指针时,也会调用其指向对象的析构函数。避免内存泄漏。如果一个class带有一个或多个virtual函数,它就应该拥有一个virtual析构函数。
(2)、classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不应该声明virtual函数。(会多出一个vptr指针,占用内存),string及STL容器均为无virtual函数!尽量别继承它们做事。
引申:c++11中可以override,final关键字指定。只能作用于虚函数
override,表示此虚函数必定“重写”了基类中的对应虚函数。
final,(1)作用在虚函数:表示此虚函数已处在“最终”状态,后代类必定不能重写这个虚函数。
(2)作用在类:表示此类必定不能被继承
编译器将帮你检查是否“必定”
08、别让异常逃离析构函数
(1)、析构函数绝对不要抛出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后处理它们,或者结束程序;
(2)、如果接口使用者需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作;
class DBConn
{
public:
void close()
{
db.close()//可能抛出异常函数
closed = true;
}
~DBConn()
{
if (!closed) //如果客户没有主动关闭的话
{
try
{
db.close();
}
catch(...)
{
//制作运转记录,记下对close的调用失败
}
/* code */
}
}
private:
DBConnection db;
bool closed;
};
09、不要在构造和析构过程中调用virtual函数
class Transaction //基类
{
public:
Transaction();
~Transaction();
virtual void logTransaction const = 0;
};
Transaction::Transaction()
{
...
logTransaction();//记录这笔交易
}
class BuyTransaction : public Transaction
{
public:
BuyTransaction();
~BuyTransaction();
virtual void logTransaction()const override; //记录这笔交易
};
BuyTransaction b;时先执行Transaction::Transaction,此时传入的this指针为Transaction* 故调用的是Transaction::logTransaction(),此时BuyTransaction还没被构造出来。
构造函数:base::base-->derive::derive
析构函数:base::~base-->derive::~derive
10、令operator=返回一个reference to *this
用于链式赋值
11、在operator= 中处理“自我赋值”
推荐做法:
Widget& Widget::operator=(Widget rhs)
{
swap(rhs); //成员函数,进行交换
return *this;
}
(1)、确保当对象进行自我赋值时operator=有良好的行为。包括考虑“来源对象”和“目标对象”的地址(是否为同一个)、精心周到的语句顺序、以及复制交换;
(2)、当一个函数操作多个对象时,确保即使这些对象为同一个对象,其行为仍然正确。
12、复制对象时勿忘其每一个成分
(1)、Copying函数应该确保复制“对象内的所有成员变量”及“所有base class”成分;
(2)、不要用某个copying函数实现另一个copying函数。应该将共同的代码放进第三个函数中,并由两个copying函数共同调用。