构造函数和析构函数,在C++中能否声明为虚函数?构造函数和析构函数里面能否调用虚函数?

本文解析了构造函数为何不能声明为虚函数,而析构函数则需声明为虚函数的原因。通过实例说明了虚析构函数如何避免内存泄漏,并探讨了在构造函数和析构函数中调用虚函数的潜在问题。

构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。

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

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

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

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

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

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

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

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

例:

#include   "stdafx.h"  

#include   "stdio.h"  

class   A      

{  

public:  

A();  

virtual   ~A();  

};  

A::A()  

{  

}  

   

A::~A()  

{  

printf("Delete   class   AP/n");  

}  

class   B   :   public   A      

{  

public:  

B();  

~B();  

};  

   

B::B()  

{   }  

   

B::~B()  

{  

printf("Delete   class   BP/n");  

}  

int   main(int   argc,   char*   argv[])  

{  

A   *b=new   B;  

delete   b;  

return   0;  

}      

   

输出结果为:Delete   class   B        

                        Delete   class   A  

   

如果把A的virtual去掉:那就变成了Delete   class   A

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

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

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

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

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

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

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

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

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

### 构造函数析构函数是否可以声明虚函数及其原因 在 C++ 中,构造函数不能声明虚函数,而析构函数可以声明虚函数。以下是具体原因相关说明: #### 构造函数不能声明虚函数 构造函数的主要职责是初始化对象的状态。在对象创建时,编译器需要明确知道调用哪个类的构造函数以正确初始化对象。如果构造函数声明虚函数,则会导致不确定性,因为虚函数机制依赖于动态绑定,而动态绑定需要通过虚函数表(vtable)来实现。然而,虚函数表本身是在对象过程中初始化的[^1]。因此,在构造函数执行之前,虚函数表尚未完全初始化,无法支持虚函数调用。 此外,构造函数的目标是创建一个完整的对象实例,而虚函数通常用于多态行为,这要求对象已经存在并可以调用其方法。由于构造函数执行时对象尚未完全建,因此不允许构造函数作为虚函数存在。 ```cpp class Base { public: Base() { /* 初始化代码 */ } }; class Derived : public Base { public: Derived() { /* 初始化代码 */ } }; ``` #### 析构函数可以声明虚函数构造函数不同,析构函数可以声明虚函数,尤其是在涉及多态的情况下。当使用基类指针或引用删除派生类对象时,若基类的析构函数不是虚函数,则只会调用基类的析构函数,而不会调用派生类的析构函数,这可能导致资源泄漏或未定义行为[^2]。将基类的析构函数声明虚函数可以确保在删除对象时正确调用整个继承链中的析构函数,从而安全地释放所有资源。 ```cpp class Base { public: virtual ~Base() { /* 释放资源 */ } }; class Derived : public Base { public: ~Derived() { /* 释放资源 */ } }; void deleteObject(Base* obj) { delete obj; // 如果 Base 的析构函数不是虚函数,Derived 的析构函数不会被调用 } ``` #### 示例代码 以下是一个示例,展示了析构函数的重要性: ```cpp #include <iostream> using namespace std; class Base { public: Base() { cout << "Base Constructor" << endl; } virtual ~Base() { cout << "Base Destructor" << endl; } // 析构函数 }; class Derived : public Base { public: Derived() { cout << "Derived Constructor" << endl; } ~Derived() { cout << "Derived Destructor" << endl; } }; int main() { Base* obj = new Derived(); delete obj; // 调用析构函数确保 Derived Base 的析构函数都被调用 return 0; } ``` #### 原因总结 - 构造函数不能声明虚函数,因为在构造函数执行时虚函数表尚未初始化,无法支持动态绑定。 - 析构函数可以声明虚函数,以确保在通过基类指针或引用删除派生类对象时,整个继承链中的析构函数都能被正确调用,避免资源泄漏。 ### 注意事项 即使析构函数可以声明虚函数,也应谨慎使用。如果类不打算作为基类使用,则无需将析构函数声明虚函数,以减少不必要的开销[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值