构造,析构, 和重载赋值运算符
# Item 05:Know what functions C++silently writes and calls. 了解C++默默编写并调用哪些函数
C++ 自动生成的ctor,dtor, copy assignment(op=重载),(big three, 除此之外还有移动构造, 移动赋值, 不过这需要自己的写)
class Empty{ public: Empty() { ... } Empty(const Empty& rhs){ ... } ~Empty() { ... } Empty& operator=(const Empty& rhs) { ... } } int main(){ Empty e1; Empty e2(e1); e2 = e1; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15默认的构造, 析构函数, 只能是浅复制, 值的复制, 如果有指针, 只能指向同一个地址, 而不能有两份内存出来;
对const成员变量, reference变量, 自动生成的copy assignment是不能对这些东西进行赋值和拷贝操作的
class Timekeeper{ public: Timekeeper(); virtual ~TimeKeeper(); }
1
2
3
4
5经验: class里面如果有一个virtual的函数, 就为这个类弄一个virtual的析构函数;
纯虚函数?纯虚函数会带来一个抽象基类, 抽象基类不能实例化;但是抽象基类要有一个纯虚的析构函数,需要在H文件里面单独提供一份定义;这样在析构的时候就不会显式运行到在这个纯虚函数;
让其他的子类在析构的时候, 会使用到子类自己的析构函数, 而不是默认的析构, 或者是纯虚基类的析构(纯虚基类的纯虚析构函数, 是空的)
class AWOV(){ public: virtual ~AWOV() = 0; } AMOV::~AMOV() { } // 在.h文件里面单独提供一份定义, 因为虽然是纯虚函数, 但是子类在析构的时候,还是会调用到这个
1
2
3
4
5
6
# Item 06:Explicitly disallow the use of compiler-generated functions you do not want. 若不想使用编译器自动生成的函数,就该明确拒绝
如何拒绝使用这些自动生成的函数,
- 使用=delete来禁止使用拷贝构造, 拷贝赋值,
- 以前在没有=delete关键字的时候, 可以把这些函数放在private里面
# Item 07:Declare destructors virtual in polymorphic base classes. 为多态基类声明virtual析构函数
多态基类的析构函数最好弄成virtual的, 如果是non-virtual的,那么借由基类指针传进来的对象, 析构的时候, 派生出来的部分会被漏掉
- 如果析构函数不是virtual的, 那么delete基类指针来进行析构的话。derived部分偶尔会被遗漏掉;
- 为什么不是virtual的构造函数?因为我们构造的时候, 基本上都是用derived类来进行构造的;
问题: 对于virtual function, 如何进行派生使用, 如何借由virtual函数来进行多态?
# Item 08:Prevent exceptions from leaving destructors. 别让异常逃离析构函数
析构函数不要吐出异常(别让异常逃离析构函数), 为什么?
- 如果不处理异常, 会带来一些不明确的行为, 或者程序半路断掉
- 所以要好好善后;怎么办,如果析构函数真的抛出异常, 就在析构函数里面进行try{} catch{} , 要么记录下来,立即停止程序,不重要的异常可以不停下来, 让程序继续进行;也可以用单独的函数来处理异常, 但总之就是在类内部, 处理好异常, 不要让异常逃出析构, 这样可以封装得更完整
# Item 09:Never call virtual functions during construction or destruction. 绝不在构造和析构过程中调用virtual函数
构造和析构函数中, 不要调用virtual函数, 为什么?, static函数相关的使用场景和效果?
- 构造的时候, derived对象还没有生成, 所以这个时候还用不到自身特化的virtual function, 只能用base class里面的版本;
- 析构的时候, 只要一开始析构, 对象内的derived class成员变量就是未定义值, 所以这个时候virtual function还是会跑到父类里面去;
- 父类里面的static函数, 有什么作用? 是在函数编译的时候, 放在什么段里面?(很大程度和python的static method类似, 能通过类名调用) static函数里面不能调用non-static成员变量, 也不能调用non-static成员函数; static函数没有this指针
# Item 10:Have assignment operators return a reference to*this. 令operator=返回一个 reference to *this
operator= 最好是返回一个 reference to *this,
- 因为这样可以拥有连续赋值的特性, 例如: x = y = z = 15
# Item 11:Handle assignment to self in operator=. 在operator=中判断“自我赋值”
Widget& Widget::operator=(const Widget& rhs){
if(this == &rhs)
return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *rhs;
}
2
3
4
5
6
7
# Item 12:Copy all parts of an object. 复制对象时勿忘其每一个成分
自定义拷贝构造函数的时候, 要复制所有local的变量, 可以使用基类的一些拷贝构造函数对一些成员拷贝;
因为有一些基类对象的拷贝, 必须要通过基类的拷贝构造函数来进行深度拷贝;
PriorityCustomer::operator=(const PriorityCustomer &rhs){ logCall("......"); Customer::operator=(rhs); // 这里直接调用了base class的拷贝构造函数来进行; priority = rhs.priority; return *this; }
1
2
3
4
5
6不要使用copy assignment 来调用copy constructor; 反过来也不建议, 因为这就像对一个构造好的对象进行操作, 先有鸡还是先有蛋?
最终还是要将他么共同的一些实际操作放到函数里面, 因为只要使用上面两种函数, 都会要实现构造一个临时的对象, 但对象在这些函数运行过程中都是没有构造好的;