`

构造函数和析构函数能否声明为虚函数?

    博客分类:
  • C++
 
阅读更多

转自<http://blog.csdn.net/lmsnju/article/details/5386617>

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

 

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

 

 

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

 

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

 

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

 

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

 

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

 

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

 

解释:在类的继承中,如果有基类指针指向派生类,那么用基类指针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. 记得在写派生类的拷贝函数时,调用基类的拷贝函数拷贝基类的部分,不能忘记了。

 

 

 

如果一个类是作为基类使用,那么他的虚构函数一定要是虚的,即用virtual关键字(参数为零则为纯虚函数).
  否则会有内存泄漏(很重要),因为当用基类的指针删除一个派生类的对象时,要调用派生类的析构函数.但是
  其子类或者子子类可以的析构函数可以是虚函数,也可以不是虚函数.(不加virtual 关键字则不会调用派生
  类的析构函数,而上面用了ClxBase *pTest = new ClxDerived;语句也就是new的ClxDerived对象没有   销毁,所以产生内存泄漏)
2.类中的虚函数,如果一个类中的函数被声明成为虚函数,那么其子类不用在声明为虚函数(当子类还有子类时),
  也可以声明为虚函数.结果是一样的.同虚析构函数的道理是一样的.当然,并不是要把所有类的析构函数都写
  成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就
  会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

 

 

 

具体例子:

 

#include "iostream.h"
 
class ClxBase
{
public:
 ClxBase() {};
 virtual ~ClxBase() { cout << "Output from the destructor of class ClxBase!" << endl; };
 virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};

class ClxDerived : public ClxBase
{
public:
 ClxDerived() {};
 virtual~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };

 //此处的virtual可以去掉
 virtual void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };

 //此处的virtual可以去掉
};

class ClxThrived : public ClxDerived
{
public:
 ClxThrived(){};
 virtual ~ClxThrived(){cout << "Output from the destructor of class ClxThrived!" << endl;};

 //此处的virtual可以去掉
 virtual void DoSomething(){cout << "Do something in class ClxThrived!" << endl;}

 //此处的virtual可以去掉
};

void main()
{
 ClxBase *pTest1 = new ClxBase;
 pTest1->DoSomething();
    delete pTest1;//1

 ClxBase *pTest2 = new ClxDerived;
 pTest2->DoSomething();
    delete pTest2;//2 用基类的指针删除一个派生类的对象时

 ClxDerived *pTest3 = new ClxDerived;
 pTest3->DoSomething();
    delete pTest3;//3

 ClxBase *pTest4 = new ClxThrived;
 pTest4->DoSomething();
    delete pTest4;//4 用基类的指针删除一个派生类的对象时

 ClxDerived *pTest5 = new ClxThrived;
 pTest5->DoSomething();
    delete pTest5;//5 用基类的指针删除一个派生类的对象时

 ClxThrived *pTest6 = new ClxThrived;
 pTest6->DoSomething();
    delete pTest6;//6
}

 

 

分享到:
评论

相关推荐

    C++构造函数,复制构造函数和析构函数专题[1].pdf

    C++构造函数,复制构造函数和析构函数专题[1].pdf C++继承,虚函数与多态性专题.pdf

    C++_虚函数和纯虚函数区别

    一个类的虚函数在它自己的构造函数和析构函数中被调用的时候,它们就变成普通函数了,不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”。

    C++ 课程作业 继承与派生 (motorcycle类设计(虚基类))

    声名一个基类vehicle,有私有成员maxspeed和weight,公有成员run...注意:构造函数和析构函数中均为cout语句,说明哪个构造/析构函数被调用。 该题重点和难点在于构造函数的设计,需考虑怎么给基类及最远基类传递参数。

    C++继承,虚函数与多态性专题

    本文分两部分即继承和虚函数与多态性,本文第一部分详细讲解了继承时的构造函数和析构函数的问题,父类与子类的 同名变量和函数问题,最后介绍了多重继承与虚基类。本文第二部分重点介绍了虚函数与多态性的问题,...

    c++实现虚基类

    构造函数和析构函数 (2) 从人员类Person派生学生记录类StudentRecord: 添加公有成员:学号(Number),班级(ClassName), 添加静态公有成员:学生总人数(TotalCount); 添加保护成员:平均成绩(Score); ...

    C++构造和析构的多态

    C++的多态是通过虚表指针来实现的。但是在构造和析构函数中调用虚函数,情况如何呢?是否还能实现多态呢?本文档从内存角度揭示了这其中的多态实现过程。

    c++模拟测试题

    c++基础练习题 类有两个特殊的成员函数,构造函数和虚函数。...Y 17、类的构造函数和析构函数都可以是虚函数。 错 18、运算符重载属于静态多态。 正确 19、从逻辑上分析,除类的静态成员之外,所有其他

    详解C++中如何将构造函数或析构函数的访问权限定为private

    主要介绍了详解C++中如何将构造函数或析构函数的访问权限定为private的方法,文中还解释了构造函数与虚函数的区别,需要的朋友可以参考下

    C++面向对象技术完全剖析_源代码(继承,封装,多态,虚函数,纯虚函数,虚拟继承,多重继承,函数重载,指针……)

    要求:1、虚函数 多态 多态表现为 基类 基类指针和继承间的关系 2、带有多对象成员。定义 3、体现继承 虚拟继承(要通过至少三层 父类父类子类) 虚函数 (3层 纵向关系) 水平方向上:体现出继承顺序 先虚拟继承 ...

    C++学习笔记、常见面试知识点.zip

    帮助了解C++的特性,包括类、构造函数和析构函数、继承、多态、虚函数、重写和重载、友元函数、符号重载、模板、文件读写、new\malloc\数组内存分配、异常、string类、命名空间、预处理器、智能指针、const和static...

    关于C++多态性的一些结论

    c++中关于在虚函数或重载中构造函数、拷贝构造函数和析构函数在派生类和基类中的调用情况

    c++基础语法:构造函数与析构函数

    说实话c++还是以前在学校的时候用过的,从毕业到现在一直用...现在主要看看继承中的构造函数和析构函数的调用: 代码如下:class A {} ;class B : public A{};class C : public B{}; c * ptr = new C() ;delete

    C++的概念/解释,可打印,华南师范大学C++考过。

    20. 派生类的构造函数和析构函数的构造规则 7 21. 虚函数及其作用 7 22. 静态关联和动态关联 7 23. 函数重载与虚函数的不同 7 24. 虚析构函数 8 25. 纯虚函数 8 26. 抽象类 8 27. 抽象类与接口的区别 8 28. 32.输入...

    虚函数被类的构造析构函数和成员函数调用虚函数的执行过程

    代码如下:#include class base{public:  base() { std::cout&lt;&lt;std::endl; std::cout&lt;&lt;“base constructor”&lt;&lt;std::endl; func1(); std::cout&lt;&lt;std::endl; } ... func

    实验四类的继承与多态实验.doc

    4.能够在派生类中使用构造函数和析构函数 5.学习虚基类在解决二义性问题中的作用。 6.熟悉多态分类,理解静态联编和动态联编概念。 7.掌握运算符重载方法。 8.理解虚函数、纯虚函数和抽象类概念。 9.掌握用...

    实验4继承与派生(4学时)

    (2)定义一个基类BaseClass,有整型成员变量Number,构造其派生类DerivedClass,观察构造函数和析构函数的执行情况。 (3)定义一个车(vehicle)基类,具有MaxSpeed、Weight等成员变量,Run、Stop等成员函数,由此...

    C++上机实验报告-实验五.docx

    编写程序定义一个基类BaseClass,构造其派生类DerivedClass,在构造函数和析构函数中用cout输出提示信息,观察构造函数和析构函数的执行情况。程序名:lab7_2.cpp。 3.用debug功能跟踪程序lab7_2的执行过程,观察...

    黄邦勇帅C++专题讲解

    介绍:本文辑是对C++中的部分难点专题进行分章的专门讲解,不是一套完整的教材,但可以做...C++构造函数,复制构造函数和析构函数专题 C++的String类及其成员函数和智能指针专题 C++的IO(输入输出)专题 C++操作符重载专题

    C++编程思想习题

    4.5含有构造函数和析构函数的stack 4.6集合初始化 4.7缺省构造函数 4.8小结 4.9练习 第5章 函数重载与缺省参数 5.1范围分解 5.1.1用返回值重载 5.1.2安全类型连接 5.2重载的例子 5.3缺省参数 5.4小结 5.5练习 第6章 ...

    C++大作业-银行管理系统课程设计报告

    (1)至少包含5个类(包含必要的构造函数和析构函数),使用面向对象程序设计思想实现; (2)要用到继承与派生,要用到多态(函数重载和虚函数都要有); (3)要有菜单选择项; (4)功能完整,设计简单的菜单...

Global site tag (gtag.js) - Google Analytics