1.构造函数与析构函数执行顺序:
(1) 继承类首先从基类构造函数开始执行,然后调用成员对象的构造函数,最后调用自己的构造函数(中间可能存在多个层次),;
(2)析构函数的执行顺序与构造函数相反,析构函数调用之后会调用成员对象的析构函数;
(3)成员对象的构造顺序是声明顺序,不是构造函数的初始化列表顺序,这样可以保证顺序的唯一性。
(4) 局部对象在程序结束或函数退出时会自动调用析构函数,但是分配在堆中的对象(new)不会,除非显示调用delete。
借用代码说明:
#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace::std;
class C
{
public:
C() {cout<<"C";}
~C(){cout<<"~C";}
};
class A
{
public:
A() {cout<<"A";}
~A(){cout<<"~A";}
};
class B : public A
{
public:
//B(A &a, C &c):_a(a), _c(c) {cout<<"B";}
B() {cout<<"B";}
~B(){cout<<"~B";}
private:
C _c;
A _a;
};
int main(int argc, char**argv)
{
//C *d=new C();
//A *e=new A();
C c;
A a;
//B b(a, c);
//B b(*d, *e);
B b;
return 0;
}
输出结果为:CAACAB~B~A~C~A~A~C
构造c和a输出CA,B从基类A开始构造输出A,然后是其成员对象_c和_a按照声明顺序构造CA(如果调整声明顺序会发现输出顺序相反),然后调用B自身的构造函数。
main函数退出,析构局部对象,从B开始首先B的析构函数~B,然后成员对象_c和_a,析构顺序与声明顺序相反~A~C(如果调整声明顺序会发现输出顺序相反),然后B的基类调用析构函数~A,然后a和c分别调用析构函数~A~C。
如果main函数使用对象d和e(注释a和c),因为是分配在堆中,所以函数退出时候不会释放d和e,即不会调用析构函数,输出结果变为CAACAB~B~A~C~A。
同时,如果B的构造函数采用注释行的方式(main函数中b使用B b(a, c)),对象_a和_c的赋值方式是采用拷贝函数,不会调用构造函数,因此会少输出CA一次,输出结果为CAAB~B~C~A~A~A~C。但是在对象已经建立,因此在析构的时候,析构函数照常调用。
2. 虚函数与构造函数和析构函数
(1) 构造函数本身不能是虚拟函数;并且虚机制在构造函数中不起作用(在构造函数中的虚拟函数只会调用它的本地版本)。
(2) 析构函数本身常常要求是虚拟函数;但虚机制在析构函数中不起作用,因为可能会引起调用已经被delete掉的类的虚拟函数的问题。
#include <iostream>
#include <stdio.h>
using namespace::std;
class Base
{
public:
Base()
{
init();
}
virtual void init()
{
printf("init base\n");
}
};
class Super : public Base
{
public:
Super()
{
init();
}
virtual void init()
{
printf("init super\n");
}
};
int main(int argc, char**argv)
{
Base *ptr=new Super();
return 0;
}
输出结果为:
init super