虚函数

虚析构函数

十年热恋 提交于 2019-12-07 07:10:21
直接的讲,C++中基类采用virtual虚析构函数是为了防止内存泄漏。 具体地说, 如果派生类中申请了内存空间 ,并在其 析构函数中 对这些内存空间进行 释放 。假设 基类 中采用的是 非虚析构函数 ,当delete 基类指针指向的派生类对象时 就不会触发 动态绑定 ,因而 只会调用基类的析构函数 ,而 不会调用派生类的析构函数 。那么在这种情况下, 派生类中申请的空间就得不到释放从而产生内存泄漏 。如果基类中析构函数声明为虚函数,则delete基类指针指向的派生类对象时通过动态绑定能够调用派生类的虚构函数,先执行派生类析构函数的函数体,再执行基类析构函数的函数体。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。 示例 基类析构函数未声明虚函数 #include <iostream> using namespace std; class Base { public: ~Base(){cout<<"~Base()"<<endl;} }; class Derived:public Base { public: virtual ~Derived(){cout<<"~Derived()"<<endl;} }; void test() { Base* b=new Derived(); delete b; cin.get(); } 输出

为什么基类中的析构函数要声明为虚析构函数?

為{幸葍}努か 提交于 2019-12-07 07:05:29
题目 为什么基类中的析构函数要声明为虚析构函数? 解答 用对象指针来调用一个函数,有以下两种情况: 如果是虚函数,会调用派生类中的版本。 如果是非虚函数,会调用指针所指类型的实现版本。 析构函数也会遵循以上两种情况,因为析构函数也是函数嘛,不要把它看得太特殊。 当对象出了作用域或是我们删除对象指针,析构函数就会被调用。 当派生类对象出了作用域,派生类的析构函数会先调用,然后再调用它父类的析构函数, 这样能保证分配给对象的内存得到正确释放。 但是,如果我们删除一个指向派生类对象的基类指针,而基类析构函数又是非虚的话, 那么就会先调用基类的析构函数(上面第2种情况),派生类的析构函数得不到调用。 请看例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Base { public : Base ( ) { cout << "Base Constructor" << endl ; } ~ Base ( ) { cout << "Base Destructor" << endl ; } } ; class Derived : public Base { public : Derived ( ) { cout << "Derived Constructor" << endl ; } ~ Derived ( ) { cout << "Derived

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的析构函数。

虚析构函数的作用

。_饼干妹妹 提交于 2019-12-07 07:01:52
今天看书 《深度探索c++ 对象模型》 , 里边有一句话" virtual function的导入应该总是附带着一个virtual destructor的声明 ", 有点不解,查了一下资料,终于明白,写下心得 为什么虚函数要伴随着一个虚析构函数呢,很明显首先是和 虚函数 有关,虚函数是为了 实现多态 的,也就是基类指针或者引用可以指向子类对象,然后调用虚函数,程序就会根据执行期的实际的类型来选择相应的版本的虚函数,这就是多态, 那么好了,既然是父类的指针来指向子类的对象,如果 对象要析构 ,肯定是 整个对象都释放内存 的,如果 析构函数不是虚函数 ,执行的时候肯定是 选择父类的版 本,那么仅仅是释放了父类的 子对象 ,而 没有释放子类 自己独有的那一份,这下就出问题了, 因此, 出现基类有虚函数的时候,一定要把析构函数也要设成虚函数 ,这样的话,子类重写虚析构函数,释放自己独有的那一份数据,调用析构函数的时候,顺序和构造函数相反,先执行子类的析构函授,后执行父类的析构函数,这样确保所有的内存都释放掉 后记:析构函数最好不要设成纯虚, 因为纯虚函数意味着只要声明,而不需要定义,那么如果有个子类B来继承类A,A 的析构函数 设为纯虚函数 ~A() = 0;那么,B的析构函数必须要去调用A的析构函数,可是这个析构函数没定义,那就肯定出错 来源: CSDN 作者: dalleny 链接:

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

旧城冷巷雨未停 提交于 2019-12-07 07:00:39
转自: http://blog.chinaunix.net/uid-20476365-id-1942463.html 虚析构函数 析构函数的工作方式是:最底层的派生类(most derived class)的析构函数最先被调用,然后调用每一个基类的析构函数。 因为在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则结果是未定义的。运行时比较有代表性的后果是对象的派生部分不会被销毁。然而,基类部分很可能已被销毁,这就导致了一个古怪的“部分析构”对象,这是一个泄漏资源。排除这个问题非常简单:给基类一个虚析构函数。于是,删除一个派生类对象的时候就有了你所期望的正确行为。将销毁整个对象,包括全部的派生类部分。 但是,一般如果不做基类的类的析构函数一般不声明为虚函数,因为虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。典型情况下,这一信息具有一种被称为 vptr(virtual table pointer,虚函数表指针)的指针的形式。vptr 指向一个被称为 vtbl(virtual table,虚函数表)的函数指针数组,每一个包含虚函数的类都关联到 vtbl。当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的 vptr 指向的 vtbl,然后在 vtbl 中寻找合适的函数指针

析构函数是否必须为虚函数?什么情况下才应该定义析构函数为虚函数?

佐手、 提交于 2019-12-07 06:59:32
多态是面向对象的一个基本属性,包括静态多态(编译阶段)和动态多态(运行阶段),静态多态主要是指函数参数不同产生的多态性,是在编译阶段可以识别的一种多态机制,而运行时多态则主要用于基类指针指向派生类对象时,可以通过基类指针直接调用派生类的对象函数,当然这种多态是通过虚函数实现的。 虚函数的目的就是通知系统在函数调用时能够自动识别对应的类对象类型,从而能够根据指针所指类型调用对应的类对象,实现函数调用时的多态性。对于析构函数而言,同样适用于上述规则。如果析构函数不是虚函数,那么在调用该函数时(对象被删除时)则只会调用当前对象对应的类的析构函数,这对于直接定义的对象是没有什么影响的,但是对于使用基类指向派生类的指针而言,因为基类指针实际上是基类类型,所以析构时自然只会调用基类的析构函数,这就可能产生内存泄漏(因为派生类的析构函数不被调用)。所以如果确定程序中有基类指针指向派生类的问题,则必须将基类的析构函数指定为虚函数,如此才能确保NEW出来的对象被正确的DELETE。 以下是几个示例程序,用于方便理解: class ClxBase{ public: ClxBase() {}; ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;}; void DoSomething() { cout <<

虚函数必须定义(纯虚函数除外)

别说谁变了你拦得住时间么 提交于 2019-12-07 06:57:53
1. 虚函数的声明和定义 具体关于虚函数的知识不做多讲,我在定义一个抽象类时,忘了将一个虚函数声明为 纯虚函数,又没有对其定义, 导致编译报错时报错如下: undefined reference to `vtable for Fibonacci' 错误提示的很明显,就是无法生成虚函数表。 我们知道,虚函数表(地址)在定义了虚函数的类所实例化的对象内存中的第一个位置,也就是在实例化过程中生成了虚表。这个错误提示在stackflow中最常见的解答就是类中声明了虚函数,却没有定义。 总结一下虚函数声明和定义的规则如下: 类中的virtual函数,要么设为纯虚函数,要么有定义,否则无法生成虚函数表。 虚函数的可以类外定义,但是必须加上类名,类外定义不需要加virtual 声明为纯虚函数,则类为抽象类,无法实例化,进一步强调,想要实例化有虚函数的类,必须对虚函数进行定义 基类定义为虚函数,则子类同名函数也为虚函数,无论是否有virtual关键字修饰(一般声明时加virtual,便于阅读) 凡是基类定义有虚函数,则基类需要定义虚析构函数(根据上一条法则,虚析构函数要么有定义,要么纯虚,一般不设为纯虚,可以定义空白) 虚函数通过虚表实现,虚表是类实例化时生成在对象中的(虚表地址),所以如果一个类能够实例化,则其虚函数必须有定义,如果不想定义虚函数,只能声明为纯虚函数,留给子类定义。

C++中的虚析构函数、纯虚析构函数详解

主宰稳场 提交于 2019-12-07 06:57:41
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; } 这是正确的用法,会发生 动态绑定

纯虚析构函数的作用

一曲冷凌霜 提交于 2019-12-07 06:57:23
在某些类里声明纯虚析构函数很方便。纯虚函数将产生抽象类——不能实例化的类(即不能创建此类型的对象)。有些时候,你想使一个类成为抽象类,但刚好又没有任何纯虚函数。怎么办?因为抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数。   这里是一个例子:   class awov {   public:   virtual ~awov() = 0; // 声明一个纯虚析构函数   };   这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:   awov::~awov() {} // 纯虚析构函数的定义   这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。 来源: CSDN 作者: reds1892 链接: https://blog.csdn.net/reds1892/article/details/39406425

C++中虚析构函数和纯虚函数的作用

别来无恙 提交于 2019-12-07 06:56:53
一. 虚析构函数 为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在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的析构函数。 如果析构函数不加virtual,delete pb只会执行Base的析构函数,而不是真正的Derived析构函数。 因为不是virtual函数,所以调用的函数依赖于指向静态类型,即Base。 二. 纯虚析构函数 现在的问题是,我们想把Base做出抽象类,不能直接构造对象,需要在其中定义一个纯虚函数。如果其中没有其他合适的函数,可以把析构函数定义为纯虚的,即将前面的CObject定义改成: class Base { public : Base (){} virtual ~Base()= 0 }; 可是,这段代码不能通过编译,通常是link错误,不能找到~Base(