编译器版本的默认构造/析构/赋值/拷贝构造函数
- 当用户没有在类内声明上述三个函数时,如果程序中需要调用,编译器会为用户自动编写默认构造/析构/拷贝构造这三个重要的函数,即所谓的Big Three,和拷贝构造函数,并且这些函数都是inline的。在c++ 11后编译器新增了move构造和move赋值两个函数:
继承一个不可拷贝赋值和拷贝构造的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31template<class T>
class Handle {
T* p;
public:
Handle(T* pp) : p{pp} {}
// 用户定义构造函数: 没有隐式的拷贝和移动操作
~Handle() { delete p; }
Handle(Handle&& h) :p{h.p}//移动拷贝
{ h.p=nullptr; };
Handle& operator=(Handle&& h) //移动赋值
{ delete p; p=h.p; h.p=nullptr; }
Handle(const Handle&) = delete; //禁用拷贝构造函数
Handle& operator=(const Handle&) = delete;
};
```
- 一旦我们显式地指明( 声明, 定义, =default, 或者 =delete )了上述五个函数之中的任意一个,编译器将不会默认自动生成move操作。
- 一旦我们显式地指明( 声明, 定义, =default, 或者 =delete )了上述五个函数之中的任意一个,编译器将默认自动生成所有的拷贝操作。但是,我们应该尽量避免这种情况的发生,不要依赖于编译器的默认动作。
### 使用默认版本函数会发生什么?
- 编译器产生的析构函数是none-virtual的,除非class的base class自身声明有virtual构造函数。
- 编译器产生的构造函数和拷贝构造函数只是单纯将non-static成员变量拷贝到目标对象,考虑一个对象内部如果存在指针,那么只是单纯地复制指针,对于指针所指的内存区域不进行拷贝,这样的浅拷贝技术可能在后续使用过程中酿成大祸。
### 如何避免使用编译器自动生成的函数?
- 如果你不想让类支持拷贝构造或者拷贝赋值函数
使用private关键字
```cpp
class A{
private:
A(const A&);
A& operator=(const A&);
};c++11后可以使用delete关键字1
2
3
4
5
6
7
8
9
10class Uncopyable{
protected:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
class A:private Uncopyable{
};1
2
3
4class A{
A(const A&) =delete;
A& operator=(const A&) =delete;
};
基类析构函数可以是none-virtual的吗?
- 当基类析构函数带有多态性质,也就是使用基类指针调用子类函数的时候,析构函数必须是virtual类型的。
- 当需要多态的时候,一般情况下需要在堆中分配内存,这样在delete的时候就存在问题:delete的是一个父类指针,但是父类析构函数不是虚函数,那么这个子类对象中子类的部分就无法释放,这样就会存在局部销毁的情况,会导致严重的资源泄露。
- 凡是一个类中带有virtual字样的函数,一般情况下析构函数都要是虚函数。
- 当一个类不是父类的时候,析构函数尽量不要设置成虚函数。
- 因为一个类只要有虚函数,就携带虚表指针,占用额外的内存,当类内数据量很小的时候,虚表指针就回造成很大比例的内存浪费。
析构函数可以抛出异常吗?
- 一般情况下不要在析构函数中抛异常
比如在析构函数中释放多个资源,但是在释放过程中出现异常,那么剩余资源就无法被释放,就会造成内存泄露。1
2
3
4
5
6
7
8
9
10
11
12class A{
public:
vector<int> data;
~A(){
// throws...
}
};
void dosomething(){
vector<A> resource;
//...
//dtor
} - 异常处理
捕捉异常,结束程序捕捉异常,不执行任何操作1
2
3
4
5
6
7
8
9
10
11class A{
public:
vector<int> data;
~A(){
try{
//do delete
}catch(){
std::abort();
}
}
};1
2
3
4
5
6
7
8
9
10
11class A{
public:
vector<int> data;
~A(){
try{
//do delete
}catch(){
//记录
}
}
};