C++构造函数和析构函数

来源:互联网 发布:网络赚钱007秘籍联盟 编辑:程序博客网 时间:2024/05/22 03:39

原链接

http://blog.sina.com.cn/s/blog_6405313801011ybx.html

http://blog.csdn.net/xhz1234/article/details/6510568


C++中构造函数的执行顺序

经测试按照如下顺序:

一、先执行静态成员的构造函数,如果静态成员只是在类定义中声明了,而没有实现,是不用构造的。必须初始化后才执行其构造函数。

二、任何抽象基类的构造函数按照它们被继承的顺序构造(不是初始化列表中的顺序)

三、任何虚拟基类的构造函数按照它们被继承的顺序构造(不是初始化列表中的顺序)

四、任何非虚拟基类的构造函数按照它们被继承的顺序构造(不是初始化列表中的顺序)

五、任何成员对象的构造函数按照它们声明的顺序构造

六、类自己的构造函数

测试程序如下:

[cpp] view plaincopy
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. //抽象类A  
  6. class  
  7.  
  8. public 
  9.     A()  
  10.      
  11.         cout<<"抽象类A的构造函数"<<endl;  
  12.      
  13.     //纯虚函数fun  
  14.     virtual void fun1() 0;  
  15. };  
  16.   
  17. //抽象类B  
  18. class  
  19.  
  20. public 
  21.     B()  
  22.      
  23.         cout<<"抽象类B的构造函数"<<endl;  
  24.      
  25.     //纯虚函数fun  
  26.     virtual void fun2() 0;  
  27. };  
  28.   
  29. //普通类C  
  30. class  
  31.  
  32. public 
  33.     C()  
  34.      
  35.         cout<<"类C的构造函数"<<endl;  
  36.      
  37. };  
  38.   
  39. //普通类D  
  40. class  
  41.  
  42. public 
  43.     D()  
  44.      
  45.         cout<<"类D的构造函数"<<endl;  
  46.      
  47. };  
  48.   
  49. //普通类C  
  50. class  
  51.  
  52. public 
  53.     E()  
  54.      
  55.         cout<<"类E的构造函数"<<endl;  
  56.      
  57. };  
  58.   
  59. //普通类D  
  60. class  
  61.  
  62. public 
  63.     F()  
  64.      
  65.         cout<<"类F的构造函数"<<endl;  
  66.      
  67. };  
  68.   
  69. //普通类D  
  70. class  
  71.  
  72. public 
  73.     G()  
  74.      
  75.         cout<<"类G的构造函数"<<endl;  
  76.      
  77. };  
  78.   
  79. //普通类D  
  80. class  
  81.  
  82. public 
  83.     H()  
  84.      
  85.         cout<<"类H的构造函数"<<endl;  
  86.      
  87. };  
  88. //普通类D  
  89. class  
  90.  
  91. public 
  92.     M()  
  93.      
  94.         cout<<"类M的构造函数"<<endl;  
  95.      
  96. };  
  97.   
  98. class Test: public A,public B,virtual public C,virtual public D,public E,public  
  99.  
  100. public 
  101.     Test():B(),A(),D(),C(),F(),E()  
  102.      
  103.         cout<<"类Test的构造函数"<<endl;  
  104.      
  105.     void fun1()  
  106.      
  107.      
  108.     void fun2()  
  109.      
  110.      
  111. private 
  112.     g;  
  113.     static h;  
  114.     static m;  
  115. };  
  116.   
  117. Test::h;  
  118.   
  119. int main(int argc, charargv[])  
  120.  
  121.     Test test;  
  122.     return EXIT_SUCCESS;  
  123.  
结果: 类H的构造函数
类C的构造函数
类D的构造函数
抽象类A的构造函数
抽象类B的构造函数
类E的构造函数
类F的构造函数
类G的构造函数
类Test的构造函数



 

C++:构造函数和析构函数能否为虚函数

C++:构造函数和析构函数能否为虚函数?

简单回答是:构造函数不能为虚函数,而析构函数可以且常常是虚函数。

(1) 构造函数不能为虚函数

让我们来看看大牛C++之父 Bjarne Stroustrup 在《The C++ Programming Language》里是怎么说的:

To construct an object, a constructor needs the exact type of the object it is to create. Consequently, a constructor cannot be virtual. Furthermore, a constructor is not quite an ordinary function, In particular, it interacts with memory management in ways ordinary member functions don't. Consequently, you cannot have a ponter to a constructor.

--- From 《The C++ Progamming Language》15.6.2

然而大牛就是大牛,这段话对一般人来说太难理解了。那下面就试着解释一下为什么:

这就要涉及到C++对象的构造问题了,C++对象在三个地方构建:(1)函数堆栈;(2)自由存储区,或称之为堆;(3)静态存储区。无论在那里构建,其过程都是两步:首先,分配一块内存;其次,调用构造函数。好,问题来了,如果构造函数是虚函数,那么就需要通过vtable 来调用,但此时面对一块 raw memeory,到哪里去找 vtable 呢?毕竟,vtable 是在构造函数中才初始化的啊,而不是在其之前。因此构造函数不能为虚函数。

 

(2)析构函数可以是虚函数,且常常如此

这个就好理解了,因为此时 vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。

 

 

困惑我们的是我们却经常看到“虚构造函数”这样的说法,这就要归咎于不负责任或者说误人子弟的媒体了(包括书、技术文章等等)。因为他们说的是类似下面这样的做法:

class Expr {

public:

     Expr();

     Expr(const Expr&);

     virtual Expr* new_expr() { return new Expr(); }

     virtual Expr* clone() { return new Expr(*this); }

};


构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。不建议在构造函数和析构函数里面调用虚函数。构造函数不能声明为虚函数的原因是:1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。。。2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。(动态绑定是根据对象的动态类型而不是函数名,在调用构造函数之前,这个对象根本就不存在,它怎么动态绑定?)编译器在调用基类的构造函数的时候并不知道你要构造的是一个基类的对象还是一个派生类的对象。析构函数设为虚函数的作用: 解释:在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。 例:#include "stdafx.h"#include "stdio.h"class A{public:A();virtual~A();};A::A(){}A::~A(){printf("Delete class APn");}class B : public A{public:B();~B();};B::B(){ }B::~B(){printf("Delete class BPn");}int main(int argc, char* argv[]){A *b=new B;delete b;return 0;} 输出结果为:Delete class BDelete class A如果把A的virtual去掉:那就变成了Delete class A也就是说不会删除派生类里的剩余部分内容,也即不调用派生类的虚函数因此在类的继承体系中,基类的析构函数不声明为虚函数容易造成内存泄漏。所以如果你设计一定类可能是基类的话,必须要声明其为虚函数。正如Symbian中的CBase一样。Note:1. 如果我们定义了一个构造函数,编译器就不会再为我们生成默认构造函数了。2. 编译器生成的析构函数是非虚的,除非是一个子类,其父类有个虚析构,此时的函数虚特性来自父类。3. 有虚函数的类,几乎可以确定要有个虚析构函数。4. 如果一个类不可能是基类就不要申明析构函数为虚函数,虚函数是要耗费空间的。5. 析构函数的异常退出会导致析构不完全,从而有内存泄露。最好是提供一个管理类,在管理类中提供一个方法来析构,调用者再根据这个方法的结果决定下一步的操作。6. 在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。7.在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。8. 记得在写派生类的拷贝函数时,调用基类的拷贝函数拷贝基类的部分,不能忘记了。


0 0