转自<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#编程语言中,构造函数和析构函数是两个非常关键的概念,它们在对象生命周期管理中起到至关重要的作用。让我们深入探讨一下这两个概念及其在实际编程中的应用。 首先,构造函数是C#中用于初始化新创建对象的方法...
但是,基类的构造函数和析构函数不能被继承。如果在派生类中需要对新增加的成员进行初始化,则需要加入派生类的构造函数。同样,派生类也需要添加析构函数来实现一些结束工作。 当创建对象时,编译系统会自动调用...
构造函数和析构函数在C++中扮演着至关重要的角色。构造函数负责初始化对象,而析构函数则用于清理对象占用的资源。尽管两者都是对象生命周期的重要组成部分,但它们在声明为虚函数方面存在显著差异。构造函数不能...
在C++编程语言中,构造函数、复制构造函数和析构函数是面向对象特性的重要组成部分,它们在对象的生命周期管理中扮演着至关重要的角色。同时,继承、虚函数和多态性是C++实现面向对象设计的核心概念。下面将详细讨论...
在C++编程中,构造函数和析构函数是两种特殊的方法,它们在对象生命周期的不同阶段起着关键作用。本文将详细探讨这两种函数的调用顺序及其重要性。 构造函数是用于初始化新创建的对象的成员变量的函数。在C++中,...
4. **示例输出解析**:从示例程序的输出可以看出,即使在构造函数和析构函数中调用虚函数,也能够看到多态的效果,但这取决于虚函数表是否已经正确设置。如果虚函数表没有被正确初始化,则会调用基类的函数。 综上...
总的来说,理解并正确使用C#的析构构造函数和`IDisposable`接口对于编写高效、无内存泄漏的代码至关重要。通过合理地管理非托管资源,我们可以确保应用程序的稳定性和性能。在实际开发中,应优先考虑使用`using`语句...
在构造函数和析构函数中调用虚函数时,它们的行为会有所改变。尽管它们被声明为虚函数,但在这个特定的上下文中,它们的行为类似于普通函数,不会进行动态绑定。这意味着,如果在构造函数中调用了一个虚函数,它会...
声名一个基类vehicle,有私有成员maxspeed和weight,公有成员run...注意:构造函数和析构函数中均为cout语句,说明哪个构造/析构函数被调用。 该题重点和难点在于构造函数的设计,需考虑怎么给基类及最远基类传递参数。
本文分两部分即继承和虚函数与多态性,本文第一部分详细讲解了继承时的构造函数和析构函数的问题,父类与子类的 同名变量和函数问题,最后介绍了多重继承与虚基类。本文第二部分重点介绍了虚函数与多态性的问题,...
总结来说,构造函数和析构函数在 C++ 中起着根本性的作用,但它们的设计意图不同。构造函数负责创建对象,并且顺序性地调用基类和派生类的构造函数。而析构函数则处理对象生命周期的结束,利用虚函数的特性来保证...
通常,`test_virtual_fun.h`会定义类结构,而`test_virtual_fun.cpp`则会实现这些类的方法,包括构造函数和析构函数,并进行一些测试用例以展示虚析构函数的效果。 在实际编程中,如果你的基类被设计用来作为其他类...
构造函数和析构函数 (2) 从人员类Person派生学生记录类StudentRecord: 添加公有成员:学号(Number),班级(ClassName), 添加静态公有成员:学生总人数(TotalCount); 添加保护成员:平均成绩(Score); ...
- 如果一个类没有声明任何虚函数,则不会为其生成虚函数表。 - 当一个类继承自另一个含有虚函数的基类时,派生类会拥有自己的虚函数表,并且该表通常会包含基类虚函数表中的函数地址,以及派生类自身定义的虚函数...
这一原则在C++语言中尤为重要,因为它涉及到虚函数系统的本质以及类层次结构中对象的构造和析构顺序。 #### 析构函数与虚函数 在C++中,析构函数用于释放对象占用的资源。虚函数则允许在派生类中重写基类中的函数...
在C++编程中,构造函数和析构函数是类的重要组成部分,它们负责对象的初始化和清理工作。本文将深入探讨C++中构造和析构调用的顺序,以及相关的编程概念。 首先,构造函数是在创建对象时自动调用的特殊成员函数,...
虚函数的主要特征是它们在类的定义中被声明为`virtual`。当通过对象的指针或引用调用虚函数时,系统会使用虚函数表(vtable)来确定实际调用哪个函数。虚表是一个包含函数指针的数组,每个指针指向类中对应的虚函数...
因此,基类的析构函数通常声明为虚函数(`virtual`)。 在实际编程中,派生类的构造与析构机制对于实现多态性和继承关系至关重要。例如,在游戏开发中,可以有一个基本的“游戏对象”类,然后派生出“角色”、...
在本实验中,我们学习了C++中的继承和派生机制,并通过四个 experiment 了解了继承关系下的成员访问控制、继承方式、虚基类、二义性问题的解决方法,以及构造函数和析构函数的调用规则。 一、继承关系下的成员访问...