雕刻时光

just do it……nothing impossible
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1.构造函数能否声明为虚函数: 构造函数不能声明为虚函数,虚函数对应一个vptr,可是这个vptr其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vptr来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vptr呢?所以构造函数不能是虚函数。(而且现在VS的编译器一般也会报错)。

 

2.析构函数能否声明为虚函数: 析构函数可以声明为虚函数,而且有时是必须声明为虚函数,是因为删除指向派生类的基类指针的时候,如果析构函数不是虚函数,派生类的析构函数将不会被执行,直接后果的内存泄漏!!!

 

3.能否在构造函数里面调用虚函数:编译不会报错,但是最好不要这样做!在调用构造这时候类里面的变量可能还被未初始化, 虚函数这时可能会访问内存中未知的区域,比较容易出错,所以最好不要在构造函数里调用虚函数。

 

4.能否在析构函数里面调用虚函数:同理,编译不会报错,但是最好不要这样做!在调用析构的时候类里面的变量可能已经被析构, 虚函数这时可能会访问内存中未知的区域,比较容易出错,所以最好不要在析构函数里调用虚函数。

 

 

 

不建议在构造函数和析构函数里面调用虚函数。

构造函数不能声明为虚函数的原因是:

解释一:所谓虚函数就是多态情况下只执行一个。而从继承的概念来讲,总是要先构造父类对象,然后才能是子类对象。如果构造函数设为虚函数,那么当你在构造父类的构造函数时就不得不显示的调用构造。还有一个原因就是为了防错,试想如果你在子类中一不小心重写了个跟父类构造函数一样的函数,那么你的父类的构造函数将被覆盖,也即不能完成父类的构造.就会出错。

解释二:虚函数的主要意义在于被派生类继承从而产生多态。派生类的构造函数中,编译器会加入构造基类的代码,如果基类的构造函数用到参数,则派生类在其构造函数的初始化列表中必须为基类给出参数,就是这个原因。

虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。(动态绑定是根据对象的动态类型而不是函数名,在调用构造函数之前,这个对象根本就不存在,它怎么动态绑定?)

编译器在调用基类的构造函数的时候并不知道你要构造的是一个基类的对象还是一个派生类的对象。

析构函数设为虚函数的作用:    

解释:在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。

#include<stdio.h>
#include<iostream>
class A{
public:A();
       virtual~A();
        void fun1(){
           printf("123");
       }
};
A::A(){}
A::~A(){
    printf("Delete class A\n");
}

class B : public A
{
public:B();
       ~B();
       void fun2(){
           printf("123456");
       }
};

B::B(){ }
B::~B(){
    printf("Delete class B\n");
}    

class C : public B
{
public:C();
       ~C();
       void fun2(){
           printf("123456");
       }
};

C::C(){ }
C::~C(){
    printf("Delete class C\n");
}   

A *a=new C;
int main(){
    delete a;

    return 0;
}
View Code

 

http://www.cnblogs.com/huhuuu/p/3453063.html 详见这篇

因此在类的继承体系中,基类的析构函数不声明为虚函数容易造成内存泄漏。所以如果你设计一定类可能是基类的话,必须要声明其为虚函数。正如Symbian中的CBase一样。Note:

 

1. 如果我们定义了一个构造函数,编译器就不会再为我们生成默认构造函数了。

2. 编译器生成的析构函数是非虚的,除非是一个子类,其父类有个虚析构,此时的函数虚特性来自父类。

3. 有虚函数的类,几乎可以确定要有个虚析构函数。

4. 如果一个类不可能是基类就不要申明析构函数为虚函数,虚函数是要耗费空间的。

5. 析构函数的异常退出会导致析构不完全,从而有内存泄露的问题。最好是提供一个管理类,在管理类中提供一个方法来析构,调用者再根据这个方法的结果决定下一步的操作。

6. 在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。

7. 在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。

8. 记得在写派生类的拷贝函数时,调用基类的拷贝函数拷贝基类的部分,不能忘记了。

 

补充解释:1如果一个类是作为基类使用,那么他的虚构函数一定要是虚的,即用virtual关键字(参数为零则为纯虚函数).否则会有内存泄漏(很重要),因为当用基类的指针删除一个派生类的对象时,要调用派生类的析构函数.但是其子类或者子子类可以的析构函数可以是虚函数,也可以不是虚函数.(不加virtual 关键字则不会调用派生类的析构函数,所以产生内存泄漏)。

2.类中的虚函数,如果一个类中的函数被声明成为虚函数,那么其子类不用在声明为虚函数(当子类还有子类时),也可以声明为虚函数.结果是一样的.同虚析构函数的道理是一样的.当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。