多态

构造函数与析构函数中不调用虚函数

僤鯓⒐⒋嵵緔 提交于 2019-12-07 07:37:13
本文参考《effective C++》第九条款 在C++中,提倡不能在构造函数和析构函数中调用虚函数。 这是为什么呢? 首先,我们先回顾一下C++虚函数的作用。 虚函数的引入是c++运行时多态的体现,通过调用虚函数可以在运行程序时实现动态绑定,体现了面向对象编程多态的思想。 那为何提倡不能在构造函数与析构函数中不能调用虚函数。接下来我们通过代码分析在构造函数或者虚构函数中调用虚函数是否可行。 假设我们有两种商品A, B。 我们要记录着两种商品的销售记录,每当销售一种商品时,我们就要记录下来。 class item { public: item(); virtual void saleRecord() const= 0 ; //销售记录,纯虚函数 ... }; item::item() { ... virtual void saleRecord(); ... } class itemA : public item { public: itemA(); virtual void saleRecord(); ... }; class itemB : public item { public: itemB(); virtual void saleRecord(); ... }; 我们执行如下代码: itemB b; 一个derived class B 对象会被创建,

《Effective C++》item7:为多态基类声明virtual析构函数

╄→尐↘猪︶ㄣ 提交于 2019-12-07 07:34:32
今天研究了一下虚函数,解决了一直困扰的几个问题,尤其是在前段时间找工作面试的时候说不清的几个问题,早知如此,何必当初哎! Questions: (1)为什么要用虚函数? (2)为什么要定义virtual析构函数? (3)什么时候该定义virtual析构函数和什么时候不该定义virtual析构函数? Answers: (1)为什么要用虚函数? C++中的虚函数的作用主要是实现了 多态的机制 。关于多态,简而言之就是 用父类类型的指针指向其子类的实例 ,然后通过父类的指针调用实际子类的成员函数 。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。Java中的多态是通过interface和abstract来实现的,java没有virtual一说! 定义一个函数为虚函数,不代表函数为不被实现的函数。 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。 而在Java中,只要子类实现了interface中定义的函数,那么接口声明的引用一定会调用子类的这个函数,如果子类没有定义,则调用接口中的函数! 定义一个函数为纯虚函数,才代表函数没有被实现。定义他是为了实现一个接口,起到一个规范的作用,规范继承这个。类的程序员必须实现这个函数。 先看一个 不使用virtual 的例子: 该程序先定义了一个基类:TimeKeeper,基类中有一个实现了的函数print

条款7 为多态基类声明virtual析构函数

亡梦爱人 提交于 2019-12-07 07:26:45
条款7 为多态基类声明virtual析构函数 如有以下关系,不使用virtual关系会出现资源泄漏 //base class class TimeKeeper { public : virtual TimeKeeper* getTimeKeeper() { return NULL; } }; class AtomTimeKeeper: public TimeKeeper { public : AtomTimeKeeper(){ pData= new char [ 100 ]; } TimeKeeper* getTimeKeeper() { return myAtom; } private : int a; char * pData; static AtomTimeKeeper* myAtom; }; AtomTimeKeeper* AtomTimeKeeper::myAtom = new AtomTimeKeeper(); int main() { AtomTimeKeeper myAtom; TimeKeeper* pTime=myAtom. getTimeKeeper(); delete pTime; //这将引起部分内存泄漏 return 0 ; } 来源: CSDN 作者: awawsese765 链接: https://blog.csdn.net/awawsese765

条款07:为多态基类声明virtual析构函数(悬挂指针)

不想你离开。 提交于 2019-12-07 07:24:21
重点: 带多态性质的基类应该声明一个虚析构函数,如果类带有任何虚函数,它就应该拥有一个虚析构函数 类的设计目的如果不是作为基类使用,或者不是为了具备多态性,就不应该声明虚析构函数。 多态性质的体现:使用基类的指针(引用)去调用派生类的对象。 如下:使用一个base指针,指向derived对象,然后delete base指针,想要调用派生类的析构函数。此时把base的析构函数设置为virtual,使用基类指针调用的时候发生了动态绑定,所以会调用派生类的析构函数,然后再调用基类的析构函数。 引入虚函数,会导致某一类中拥有了一个虚函数指针vptr,会带来额外的4B的内存消耗(在本机上),他会指向一个vtbl(virtual table虚表)。所以如果不是为了具备多态性,使用虚函数只会造成无畏的开销。 class base { public: base(int* _val) :val(_val) {} virtual ~base() { delete val; cout << "基类析构函数调用<<endl"; } int* val; }; class derivedbase { public: //dericed(int* _val2) :base(_val2),val2(_val2) {} ---1 derived(int* _val2) :base(NULL),val2(_val2)

[Effective C++系列]-为多态基类声明Virtual析构函数

混江龙づ霸主 提交于 2019-12-07 07:23:40
Declare destructors virtual in polymorphic base classes. [原理] C++指出,当derived class对象经由一个由base class类型的指针删除时,如果这个base class 拥有一个non-virtual的析构函数,那个析构的结果将是未定义的。即通常情况下是该对象的base class成分会被析构掉,但是其derived class成分没有被销毁,甚至连derived class的析构函数也不会被调用。 于是形成一个被“局部销毁”的对象,造成资源泄漏。 [示例] 例如: class car { public : car(); ~ car(); ... }; class diesel_car : public car {…}; class solar_car: public car {…}; class electric_car : public car {…}; 当客户代码中使用汽车对象时,如果他不关心使用的是具体哪一类汽车这个细节,那么我们可以设计一个工厂函数(或者工厂类)负责创建一个汽车对象,该工厂函数返回一个base class指针或者引用,指向新生成的derived class对象: car* get_car(); 为遵守工厂函数的规矩,返回的对象必须位于heap

C++虚析构函数使用场景

送分小仙女□ 提交于 2019-12-07 07:18:52
1、什么是虚函数和多态 虚函数是在类中被声明为virtual的成员函数,当编译器看到通过指针或引用调用此类函数时,对其执行晚绑定,也就是运行时多态,即通过指针(或引用)指向的类的类型信息来决定该函数是哪个类的。通常此类指针或引用都声明为基类的,它可以指向基类或派生类的对象。 编译器对每个包含虚函数的类创建一个表(称为V TA B L E)。在V TA B L E中,编译器放置特定类的虚函数地址。在每个带有虚函数的类中,编译器秘密地置一指针,称为v p o i n t e r(缩写为V P T R),指向这个对象的V TA B L E。通过基类指针做虚函数调用时(也就是做多态调用时),编译器静态地插入取得这个V P T R,并在V TA B L E表中查找函数地址的代码,这样就能调用正确的函数使晚捆绑发生。为每个类设置V TA B L E、初始化V P T R、为虚函数调用插入代码,所有这些都是自动发生的,所以我们不必担心这些。利用虚函数,这个对象的合适的函数就能被调用,哪怕在编译器还不知道这个对象的特定类型的情况下。(《C++编程思想》) 多态指同一个方法根据其所属的不同对象可以有不同的行为,即一个接口,多种实现。 2、为什么需要使用虚析构函数? 我们先来看一个例子,如下代码你认为最后的输出会是什么呢? class A { public: ~A() { cout<<

c++ 虚析构函数的作用分析

末鹿安然 提交于 2019-12-07 07:17:55
1.为什么基类的析构函数是虚函数?    在实现多态时,当用基类指针操作派生类对象时,在析构时防止只析构基类而不析构派生类的状况发生。 什么是多态: 根据面向对象的继承规则,派生类跟基类是IS-A的关系。也就是说派生类的对象也是一个基类对象。所以基类的指针可以指向派生类的对象以便实现多态。(让基类实现多态) 亦即: 这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用; 下面看代码----------------------------------------------------------------------------------------------------------   a.第一段代码    #include<iostream> using namespace std; class ClxBase{ public : ClxBase() {}; ~ClxBase() {cout << " Output from the destructor of class ClxBase! " << endl;}; void DoSomething() { cout << " Do something in class ClxBase! " << endl; }; }; class ClxDerived : public ClxBase{

C++ 虚析构函数 纯虚析构函数 虚构造函数

南楼画角 提交于 2019-12-07 07:17:28
众所周知,在实现多态的过程中,一般将基类的析构函数设为virtual,以便在delete的时候能够多态的链式调用。那么析构函数是否可以设为纯虚呢? class CBase { public : CBase() { printf("CBase()\n"); } virtual ~CBase() = 0; }; 答案是可以,那么这样实现的目的是什么呢?当然是避免实例化。 但因为派生类不可能来实现基类的析构函数,所以 基类析构函数虽然可以标为纯虚,但是仍必须实现析构函数 ,否则派生类无法继承,也无法编译通过。 下面详细讨论: 一. 虚析构函数 我们知道,为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候,必须要通过虚函数找到真正的析构函数。 如: [cpp] view plain copy print ? class Base { public : Base(){} virtual ~Base(){} }; class Derived: public Base { public : Derived(){}; ~Derived(){}; } void foo() { Base *pb; pb = new Derived; delete pb; } 这是正确的用法,会发生动态绑定,它会先调用Derived的析构函数

C++将多态基类的析构函数声明为virtual的作用

怎甘沉沦 提交于 2019-12-07 07:10:49
如果没有将多态基类的析构函数声明为virtual 当通过基类指针构造子类对象的时候,此时使用基类来引用/表示子类: BaseClass *b = new ChildClass; 如果析构函数不是virtual的,那么调用析构函数时: delete b; 这时,调用BaseClass的析构函数,进行析构;== 问题出现:使用基类的析构函数进行析构操作,但是实际对象却是这个基类的子类。所以,这样析构只能释放基类的部分,而子类的部分没有被释放(因为基类的析构函数并没有能力去析构子类的成员部分),造成内存泄露。 == 将多态基类的析构函数声明为virtual的目的 继续上面的例子,但是将BaseClass的析构函数定义为virtual。这时, delete b 仍然通过子类BaseClass来调用析构函数。== 但是,此时析构函数是virtual的,并且基类变量实际指向的是其子类对象,因此,发生动态绑定。 ==动态绑定的结果是, delete 调用的析构函数实际是子类的析构函数。这样,通过基类进行的析构操作成功地通过动态绑定调用了子类的析构函数,能够将子类的部分析构释放掉,避免内存泄露。 总结:将析构函数声明为virtual是为了激活动态绑定,用对应子类的析构函数来执行析构操作 任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数

C++虚析构函数及纯虚析构函数

血红的双手。 提交于 2019-12-07 07:02:02
C++中析构函数可以为纯虚吗? 众所周知,在实现多态的过程中,一般将基类的析构函数设为virtual,以便在delete的时候能够多态的链式调用。那么析构函数是否可以设为纯虚呢? class CBase { public: CBase() { printf("CBase()\n"); } virtual ~CBase() = 0; }; 答案是可以,那么这样实现的目的是什么呢?当然是避免实例化。 但因为派生类不可能来实现基类的析构函数,所以基类析构函数虽然可以标为纯虚,但是仍必须实现析构函数,否则派生类无法继承,也无法编译通过。 下面详细讨论: 一. 虚析构函数 我们知道,为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候,必须要通过虚函数找到真正的析构函数。 如: class Base { public: Base(){} virtual ~Base(){} }; class Derived: public Base { public: Derived(){}; ~Derived(){}; } void foo() { Base *pb; pb = new Derived; delete pb; } 这是正确的用法,会发生动态绑定,它会先调用Derived的析构函数,然后是Base的析构函数。