说实话c++还是以前在学校的时候用过的,从毕业到现在一直用c嵌入式编程,现在重新搬出C++语法 ,如果理解上有错误的地方,还请路过的朋友多指正~~~
构造函数用来构造一个对象,主要完成一些初始化工作,如果类中不提供构造函数,编译器会默认的提供一个默认构造函数(参数为空的构造函数就是默认构造函数) ;析构函数是隐式调用的,delete对象时候会自动调用完成对象的清理工作。
现在主要看看继承中的构造函数和析构函数的调用:
class A {} ;
class B : public A
{};
class C : public B
{};
c * ptr = new C() ;
delete ptr ;
一般来说,上面的代码构造函数是先调用最根父类的构造函数,然后是次一级父类构造函数,依次而来直到派生类本身的构造函数,而且对父类构造函数的调用都是父类的默认构造函数(当然也可以显示地调用父类的非默认构造函数),也就是说派生类在构造本身之前会首先把继承来的父类成分先构造好;
对析构函数的调用是先调用派生类本身的析构函数,然后是上一层父类析构函数,直到根父类析构函数 ,当没有多态的时候,析构函数是这样调用的。
改一下上面的代码:
A * ptr = new C() ;
delete ptr ;
在多态的情况下,如果基类A中的析构函数不是虚构造函数,则当delete ptr的时候只会调用A的析构函数,不会调用B和C中的析构函数;如果A中的析构函数是虚构造函数就会调用所有的析构函数,调用顺序和一般情况一样。
再改一下上面的代码:
B *prt = new C();
delete ptr ;
在多态的情况下,如果A,B中的析构函数都不是虚析构函数,则当delete ptr的时候先调用B的析构函数,再调A的析构函数,不会调用C中的析构函数,如果A或者B中至少有一个是虚析构函数,则析构函数调用和一般情况一样。
因此总结一下规律:
CA * ptr = new CB() ;
delete ptr ;
CB是CA的子类,构造函数的调用一直是一样的,当具备多态的时候:
如果CA及其父类都不具备虚析构函数,则首先调用A的析构函数,然后调用A的父类析构函数直到根父类析构函数,不会调用A以下直到派生类的析构函数 ;如果如果CA及其父类只要有一个具备虚析构函数,则析构函数调用跟一般情况一样。
因此:带有多态性质的基类应该声明虚析构函数 ,这样的基类一般还有其他虚函数;
如果类的设计不是用于基类,而且不具备多态性,则析构函数不应该声明为虚析构函数
小测试代码:
#include<iostream>
using namespace std ;
class A
{
public:
A(){cout<<"A constructor"<<endl;}
A(char * arp) { cout <<"not default " ;}
~CA(){cout<<"A desstructor"<<endl;}
};
class B:public A
{
public:
B(){cout<<"B constructor"<<endl;}
~B(){cout<<"B desstructor"<<endl;}
};
class C:public B
{
public:
C(char * arp){cout<<"C constructor"<<endl;}
~C(){cout<<"C desstructor"<<endl;}
};
int main()
{
C * ptr = new C("hello world") ;
delete ptr ;
}
另外effective C++中提到的:
1、析构函数不能吐出异常,如果析构函数掉用的函数可能产生异常,要在析构函数内部进行捕获进行处理,因为如果析构函数抛出异常的话,比如说vector,当调用各个对象的析构函数进行删除的时候可能导致抛出多个异常,从而使程序进入不确定状态。
2、如果用户需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数执行这个操作。
3、在构造函数和析构函数中都不应该调用虚函数,这是因为当调用构造函数构造对象的时候,首先会调用父类的构造函数,此时对象的类型在编译器看来就是一个父类对象(实际此时子类成员还处于不确定状态),会调用父类的虚函数,而不会调用子类的虚函数。