条款05:了解C++默默编写并调用哪些函数
一个空类,编译器会自动声明:
- 默认构造函数(对于非空类:只在没有自定义任何构造函数的时候,才会由编译器补充)
- 拷贝构造函数
- 拷贝赋值运算符
- 析构函数(非虚函数)
注意:当一个class内含有reference/const成员时,编译器不提供拷贝赋值运算符的补充,只能由程序员自己编写
这是因为C++不允许改变reference成员的指向,也不允许更改const成员
条款06:若不想使用编译器自动生成的函数,就该明确拒绝
- 问题:
比如说想要禁止一个类对象的拷贝操作,就要禁止拷贝构造函数和拷贝赋值运算符。但是不声明这两个函数,编译器可能会自动生成;要想避免编译器自动生成,又要自己声明一份;怎样都避免不了产生这两个函数。
- 解决方案1:
将拷贝构造函数、拷贝赋值运算符声明为private函数,这样在类外部不能以拷贝构造和复制的形式公然调用这两个函数
class A {
public:
A() = default;
private:
A(const A&);
A& operator=(const A&);
};
- 解决方案1存在的问题:
以class A为例,A的其他成员函数和友元函数还是能调用class A的private函数。由此引出解决方案2
- 解决方案2:
定义一个基类专门阻止拷贝动作
class unCopyable {
protected:
unCopyable() {
}
~unCopyable() {
};
private:
unCopyable(const unCopyable&) {
}
unCopyable& operator=(const unCopyable&) {
}
};
class A : private unCopyable{
public:
A() = default;
};
此时,就算是class A的成员函数和友元函数,在尝试拷贝A的对象时,编译器会“试着“生成拷贝构造函数和拷贝赋值运算符,但是这种”试着“会被基类阻止,因为基类的对应函数时private,子类不能调用
条款07:为多态基类声明virtual析构函数
当一个基类指针指向一个子类对象时,若基类的析构函数不是virtual函数,那么在delete基类指针的时候,只会释放基类对象的资源,不会释放子类对象的资源,从而造成内存泄漏
任何带有virtual函数的class都应该有一个virtual析构函数
虚函数的实现原理
-
每个含有virtual函数的class都有一个vtbl(虚函数表),vtbl中存储的是指向各个虚函数的函数指针;
-
每个对象内存空间内存在一个vptr(虚指针),这个vptr指向类的vtbl
-
当对象调用某个virtual函数的时候,编译器根据对象的vptr找到类的vtbl,在vtbl中寻找适当的函数指针
注意:不作为基类使用的class不要声明析构函数为virtual函数,因为虚表和虚表指针会占用额外的内存;同时,std::string和STL容器的析构函数都是non-virtual,不要作为基类使用