构造函数 析构函数
1.拷贝构造函数是构造函数的一种,它只有一个参数,参数类型是本类的引用。
如果编写了拷贝构造函数,则默认的拷贝构造函数就不存在了。下面是一个非默认复制构造函数的例子。
class A{ public:
//普通构造函数 A(int a,int b):_a(a),_b(b){ cout<<"this is A father"<<endl; } void get(){ cout<< _a<<" "<<_b<<endl; }
//拷贝构造函数 A(const A & a){ _a=a._a; _b=a._b; } ~A(){ cout<<"this is A ~father"<<endl; } private: int _a,_b; };
int main(){ A a(1,2); A b(a); b.get(); return 0; }
2.必须在构造函数中初始化的数据成员有:
1.引用类型
2.const修饰的类型
class people{ private: int &a; const int b; };
25.C++异常处理机制
因为构造函数没有返回值,所以通知对象的构造失败的唯一方法那就是在构造函数中抛出异常。
构造函数抛出异常不会调用析构函数,即使构造函数抛出的异常被catch,也不会掉用析构函数。
class A{ public: A(){ cout<<"this is A father"<<endl; throw 1; } ~A(){ cout<<"this is A ~father"<<endl; } };
int main(){
A *a=new A();
delete a;
}
[root@VM-32-4-centos test]# ./output/bin/main this is A father terminate called after throwing an instance of 'int' Aborted
对象中抛出异常,如果catch了,会调用析构函数,如果不catch,不会调用析构函数
class A{
public:
A(){
cout<<"this is A father"<<endl;
}
void get(){
throw 1;
}
~A(){
cout<<"this is A ~father"<<endl;
}
};
//不catch
int main(){
A a;
cout<<"main"<<endl;
a.get();
return 0;
}
[root@VM-32-4-centos test]# ./output/bin/main this is A father main terminate called after throwing an instance of 'int' Aborted
class A{ public: A(){ cout<<"this is A father"<<endl; } void get(){ throw 1; } ~A(){ cout<<"this is A ~father"<<endl; } };
//catch
int main(){
try{
A a;
cout<<"main"<<endl;
a.get();
}catch(int i){
cout<<"catch e"<<endl;
}
[root@VM-32-4-centos test]# ./output/bin/main this is A father main this is A ~father catch e
析构函数和对象均抛出异常
通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。
class A{ public: A(){ cout<<"this is A father"<<endl; } void get(){ throw 1; } ~A(){ cout<<"this is A ~father"<<endl; throw 1; } };
int main(){
try{
A a;
cout<<"main"<<endl;
a.get();
}catch(int i){
cout<<"catch e"<<endl;
}
[root@VM-32-4-centos test]# ./output/bin/main this is A father main this is A ~father terminate called after throwing an instance of 'int' Aborted
当对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构(即异常发生点前面的对象);而还没有开始构建的子对象将不会被构造了(即异常发生点后面的 对象),当然它也就没有析构过程了;还有正在构建的子对象和对象自己本身将停止继续构建(即出现异常的对象),并且它的析构是不会被执行的。 构造函数中抛出异常时概括性总结
(1) C++中通知对象构造失败的唯一方法那就是在构造函数中抛出异常;
(2) 构造函数中抛出异常将导致对象的析构函数不被执行;
(3) 当对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构;
当类C继承类A 类B时,如果B构造函数发生了异常,那么C对象不会构造。已经构造完成的A会发生析构。
class A{ public: A(){ cout<<"this is contructor AAA"<<endl; } ~A(){ cout<<"this is xigou AAAA"<<endl; } }; class B{ public: B(){ cout<<"this is contructor BBB"<<endl; throw 1; } ~B(){ cout<<"this is xigou BBBB"<<endl; } }; class C: public A,public B{ public: C(){ cout<<"this is contructor CCC"<<endl; } ~C(){ cout<<"this is xigou CCCC"<<endl; } }; int main(){ C *c=NULL; try{ c=new C(); }catch(int e){ cout<<"catch error"<<endl; } delete c; }
结果:
this is contructor AAA this is contructor BBB this is xigou AAAA catch error
总结:
析构函数C++11 编译器是默认给加上noexcept的,所以析构函数不抛出异常,如果抛出程序直接终止。
3.
移动构造函数与拷贝构造不同,它并不是重新分配一块新的空间,将要拷贝的对象复制过来,而是"偷"了过来,将自己的指针指向别人的资源,然后将别人的指针修改为nullptr
,这一步很重要,如果不将别人的指针修改为空,那么临时对象析构的时候就会释放掉这个资源,"偷"也白偷了。下面这张图可以解释copy和move的区别。
4.
类中静态变量的初始化需要在类外进行。
私有化构造函数方式可以避免在类外初始化这个类的对象,只能通过静态成员函数+静态成员变量来初始化这个对象,并通过指针的形式返回这个对象。
如果需要实现单例模式还需要私有化拷贝构造函数
私有化delete可以避免在栈上创建对象,因为栈上创建对象系统无法调用析构函数,只能在堆上创建并在类内析构,可以调用静态函数来析构。
出现 student s;会报错。只能使用new来创建
5.在构造函数里调用另一个构造函数的关键是让第二个构造函数在第一次分配好的内存上执行,而不是分配新的内存
构造函数调用自身是不允许的,会出现无限递归调用。
#include <stdlib.h> #include <iostream> using namespace std; struct CLS { int m_i; CLS( int i ) : m_i(i){} CLS() { CLS(0); } }; int main() { CLS obj; cout << obj.m_i << endl; system("PAUSE"); return 0; }
6.构造函数调用虚函数,会失去多态性
一般情况下,不允许在构造函数或者析构函数中调用虚函数。其实语法上都没有问题,只是会失去多态性。
如果在构造函数中调用虚函数,会先调用父类中的实现,也就失去了多态的性质。
构造函数还没有将vptr和vtable初始化完毕,就调用虚函数,此时必然调用的基类中的实现。
7. 拷贝构造函数/构造函数/赋值重载运算符,私有的目的都是为了外部禁用。
8.移动构造函数
参数是 && 右值 的构造函数
class ArrayWrapper { public: // default constructor produces a moderately sized array ArrayWrapper () : _p_vals( new int[ 64 ] ) , _size( 64 ) {} ArrayWrapper (int n) : _p_vals( new int[ n ] ) , _size( n ) {} // 移动构造函数是使自己的指针指向这块右值的临时空间空间,同时为了防止右值的临时内存被析构,需要重置指针为NULL; ArrayWrapper (ArrayWrapper&& other) : _p_vals( other._p_vals ) , _size( other._size ) { other._p_vals = NULL; } // 拷贝构造函数重新开辟了一块内存空间,并往里面赋值 ArrayWrapper (const ArrayWrapper& other) : _p_vals( new int[ other._size ] ) , _size( other._size ) { for ( int i = 0; i < _size; ++i ) { _p_vals[ i ] = other._p_vals[ i ]; } } ~ArrayWrapper () { delete [] _p_vals; } private: int *_p_vals; int _size; };