这个警告是由GNU编译器生成的,表示在类中定义了虚函数,但是没有声明虚析构函数。这可能会导致潜在的问题,例如当对象被销毁时,虚函数表可能不会被正确地更新。为了解决这个问题,可以在类的析构函数前加上virtual关键字,将其声明为虚析构函数。
例如:
class MyClass { public: virtual ~MyClass() {} // 其他代码 };
这样,编译器就不会再生成这个警告了。
; 自身定义的 析构函数 ; 然后 , 调用 成员 析构函数 ; 也就是 成员变量 类型的 析构函数 ; 最后 , 调用 父类 析构函数 ; 2、析构函数可以是虚函数 析构函数 可以是 虚函数 ; 虚析构函数...; 释放 A 类型的指针 , 需要调用其子类 B 类型对象的 析构函数 , 此时需要将 A 类型 和 B 类型的 析构函数 声明为 虚析构函数 ; 3、虚析构函数语法 虚析构函数 的 语法 是 在 父类...~Base() {} }; 4、虚析构函数意义 父类中使用了 虚析构函数 , 在 子类 中 , 必须 覆盖 父类 的虚析构函数 , 并且使用相同的函数签名 ; 如果 子类 没有提供自己的 析构函数..., 则编译器会自动生成一个 析构函数 , 该 析构函数 会首先调用 父类 的 析构函数 , 然后执行 子类 的 析构函数 ; 使用 虚析构函数 的目的是 确保在释放 子类 对象时正确地释放资源和调用析构函数..., 只有在 父类 的析构函数是 虚函数 时 , 子类 的析构函数才必须是虚函数 ; 如果 父类 的 析构函数 不是 虚函数 , 则 子类 的 析构函数 可以是 普通的 非虚函数 ; 二、代码示例 -
虚函数仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数。 2、静态成员函数不能是虚函数静态成员函数不受对象的捆绑,只有类的信息。 3、内联函数不能是虚函数。...4、构造函数不能是虚函数,构造时,对象的创建尚未完成。构造完成后,能算一个名符其实的对象。 5、析构函数可以是虚函数且通常声明为虚函数。...(2, 4, 8); s->draw(); // delete 指针,调用析构函数 delete s; return 0; } 以上代码演示了纯虚函数的定义,但上面代码存在一个问题,我们先看一下运行的结果...,初始化为 0,提供族类的公共接口 virtual void draw() = 0; // 增加 virtual 关键字,让其自动执行子类析构函数 virtual ~Shape() { cout <...< “Shape destructor” << endl; } protected: int _x; int _y; }; 这样修改代码后,我们再次运行,结果就能看到,Circle 正常被析构了。
C.127: A class with a virtual function should have a virtual or protected destructor C.127:包含虚函数的类应该有虚析构函数或保护析构函数...包含虚函数的类通常(大多数情况下)通过指向基类的指针使用。通常,最后一个使用者必须通过指向基类的指针调用delete操作,通常是指向基类的智能指针,因此析构函数应该是公开的虚函数。...稍微特殊一些的情况是:如果不希望支持通过指向基类的指针销毁对象,析构函数应该是保护的非虚函数。参见C.35。...包含虚函数的类的析构函数要么是公开的虚函数,要么是保护的非虚函数。...提示针对包含虚函数却没有虚析构函数的类的销毁操作。
如果析构函数是公有的,那么调用侧的代码就会尝试使用基类指针销毁派生类的对象,在基类的析构函数为非虚函数时其结果时没有定义的。...如果析构函数时保护的,那么调用侧代码就无法通过基类类型指针销毁派生类对象,这是析构函数就没有必要一定是虚函数。析构函数是保护而不是私有的,这样派生类的析构函数才能调用它。...通常,基类的设计者不会知道在析构函数中应该执行什么样的动作。...我们可以想象一种需要保护的虚函数析构函数的情况:当希望允许派生类的对象(只有这个类型)通过基类指针销毁另外一个对象(不是它自己)时。但是我们还没有在实际的开发中遇到这种情况。...拥有虚函数的类的虚函数要么是公开的虚函数,要么是保护的非虚函数。 译者注:拥有虚函数一般就意味着它有派生类。
Shape类是抽象类,Draw函数是纯虚函数,Circle, Square, Rectangle都重新实现了Draw,在这里把Shape的析构函数声明为虚函数,那么delete 基类指针,会调用派生类的析构函数...,并能管理该对象 可以添加新类(已有类的派生类)的新对象,并能管理该对象 四、虚析构函数 析构函数可以声明为虚函数 delete 基类指针; 程序会根据基类指针指向的对象的类型确定要调用的析构函数...基类的析构函数为虚函数,所有派生类的析构函数都是虚函数 构造函数不得是虚函数 如果要操作具有继承关系的类的动态对象,最好使用虚析构函数。...特别是在派生类析构函数需要完成一些有意义的操作,比如释放内存 析构函数还可以是纯虚的。... Derived d; return 0; } // 对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚析构函数声明为纯虚的 // 通常情况下在基类中纯虚函数不需要实现 // 例外是纯虚析构函数要给出实现
这个要提一下,如果记不住就记住:如果不做虚析构函数,会有内存泄漏 解释 定义一个基类的指针p,在delete p时,如果基类的析构函数是虚函数,这时只会看p所赋值的对象,如果p赋值的对象是派生类的对象,...就会调用派生类的析构函数;如果p赋值的对象是基类的对象,就会调用基类的析构函数,这样就不会造成内存泄露。...如果基类的析构函数不是虚函数,在delete p时,调用析构函数时,只会看指针的数据类型,而不会去看赋值的对象,这样就会造成内存泄露。 多少学点设计模式就清楚了。...Inherit :public Base{ //此处省去,一切从简 }; //重点看调用 int main() { Base *p = new Inherit; //这种方式的调用,这时候有没有虚析构就不一样了
款5、了解C++默认编写并调用哪些函数 通常情况下,如果代码中没有声明构造函数、拷贝构造函数、拷贝运算符、析构函数,编译器会在需要时创建他们,但这往往只能满足编译器的需求,很可能无法满足程序的需求.../析构函数;生成的析构函数是非虚的,除非基类有虚析构函数。...当这样的一个指向派生类的基类指针析构时,如果析构函数不是虚函数,则直接调用基类的析构函数,那么派生类获取的资源未释放,则会造成内存泄漏。...而当析构函数是虚函数时则先调用对应的派生类析构函数,再调用基类析构函数,资源全部释放。...进入析构函数后派生类部分呈未定义值,对象类型是基类,调用的是基类的虚函数。 总而言之,在构造函数与析构函数中虚函数的行为有特殊变化;为了避免出错,不要在其过程中使用虚函数。
5、 在类继承机制中,构造函数和析构函数具有一种特别机制叫 “层链式调用通知” 《 C++编程思想 》 C++标准规定:基类的析构函数必须声明为virtual, 如果你不声明,那么"层链式调用通知"这样的机制是没法构建起来....从而就导致了基类的析构函数被调用了,而派生类的析构函数没有调用这个问题发生....,这是因为我们将基类的析构函数声明为虚函数的原因,在pI 指向派生类首地址的前提下,如果~IRectangle() 是虚函数,那么会找到实际的函数~Rectangle() 执行,而~Rectangle...如果没有这样做的话,只会输出基类的 析构函数,这种输出情况通过比对规则2也可以理解,pI 现在虽然指向派生类对象首地址,但执行pI->~IRectangle() 时 发现不是虚函数,故直接调用, 假如在派生类析构函数内有释放内存资源的操作...将基类析构函数改成虚函数,fun() 最好也改成虚函数,只要有一个虚函数,基类大小就为一个vptr ,此时基类和派生类大小都是4个字节,p也指向派生类的首地址,问题解决,参考规则3。
在定义基类时,需要将要在派生类中重新定义的类方法声明为虚,如析构函数 除了这些之外,我们还有一些其他需要注意的事项。...一般我们会在派生类中调用基类的构造函数,这其实不是继承机制,所以将类构造函数声明为虚没有意义。 析构函数 前文说过析构函数应该是虚函数,除非类不被继承。...因为派生类当中往往含有独有的成员变量,如果析构函数非虚,那么会导致在对象析构时仅调用基类的析构函数,从而导致独有的成员变量内存不被释放,引起内存泄漏。...我试了一下,在我的g++编译器当中,会报错: 但根据C++ Primer中的说法,在一些古老的编译器当中,可能不会报错,甚至可能连警告都没有。...但这可能和编译器版本有关,在新版的编译器当中似乎取消了这一设定。 我尝试了一下,发现并不会隐藏,一样可以顺利调用父类方法。
(二)、构造/析构/赋值运算 五、C++默认编写的函数 默认构造、复制构造、析构、赋值运算符。 六、拒绝自动生成的函数 私有化拷贝构造和赋值运算符; 私有继承UnCopyable手工类。 ?...七、多态基类声明虚析构函数 (不)具有多态性质基类(不)需要虚析构函数; 八、不让异常逃出析构 异常时终止或者吞下; 将可能抛出异常的代码提供给用户管理; 九、不在构造和析构中调用虚函数 调用后仅仅是自身的虚函数...十二、复制对象要面面俱到 不要丢失基类的成员的复制。 (三)、资源管理 十三、对象管理资源 构造函数获得资源,析构函数释放资源; 使用智能指针封装:tr1::shared_ptr和auto_ptr。...三十、inline里里外外 隐式:累内直接定义成(友)员函数,显式:inline关键字; 拒绝:复杂、虚函数、函数指针调用、模板、构造析构函数、影响动态连接或升级、对调试器的挑战(禁用)。...三十六、绝不定义继承的非虚函数 重修继承的非虚函数导致函数的访问由指向对象的指针或引用类型决定。
构造/析构/赋值运算 条款05:了解C++默默编写并调用哪些函数 当没有声明时,编译器会自动为类创建默认构造函数、析构函数、复制构造函数和赋值构造函数,但如果成员变量中包含引用、const这些不能被改变的值...条款07:为多态基类声明虚析构函数 如果一个基类可能有多态子类,那么就该声明一个虚析构函数。 如果一个类有任何虚函数,那么它就应该有虚析构函数。...如果一个类不被用来做基类,那么就不该声明虚析构函数。...条款09:绝不在构造和析构过程中调用虚函数 在构造函数和析构函数中不要去调用虚函数,因为子类在构造/析构时,会调用父类的构造/析构函数,此时其中的虚函数是调用父类的实现,但这是父类的虚函数可能是纯虚函数...杂项讨论 条款53:不要轻忽编译器的警告 对于编译器编译时给出的警告信息,最好立即修复,避免后续调试半天来寻找编译器早就告知你的问题。
析构函数应该是虚函数吗?也就是说,是否应该允许通过指向基类的指针进行销毁?如果是,则base的析构函数必须是公共的才能被调用,否则虚拟调用它会导致未定义的行为。...否则,应该对其进行保护,以便只有派生类才能在自己的析构函数中调用它,这个析构函数也应该是非虚的,因为它不需要虚拟地运行。...这种情况导致较早的编码标准对所有基类析构函数都必须是虚拟的提出了全面的要求。这太过分了(即使是常见情况);相反,规则应该是当且仅当基类析构函数是公共的时,才将它们虚函数化。...析构可以看作只是另一种操作,尽管具有使非虚调用变得危险或错误的特殊语义。因此,对于基类析构函数,选择是根据是否允许通过指向Base的指针实际上调用它。“非虚”不是一种选择。...推论:编写基类时,请始终显式编写一个析构函数,因为隐式生成的是公共的和非虚的。如果默认函数就很好,那么您只需要决定器可见性和虚函数性,则实现可以直接使用=default。
class HomeForSale: private Uncopyable{...}; 7:为多态基类声明virtual析构函数 1.带有多态性质的基类应该声明一个虚析构函数。...如果一个类里面有任何虚函数,那么它也应该有一个虚析构函数。 2.如果一个类的设计目的不是为了多态,那么就不该声明virtual析构函数。...8:别让异常逃离析构函数 1.析构函数绝对不要吐出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们或结束程序。...9:绝对不要在构造和析构过程中调用虚函数 当构造子类的时候,需要先去调用父类的构造函数,这时候子类还不存在,是无法去自动调用子类的虚函数的。...49-52未读 杂项讨论 53:不要轻忽编译器的警告 1.严肃对待编译器发出的警告信息。努力在你的编译器的最高警告级别下争取无任何警告。
6 特殊成员函数 构造函数和析构函数 正如我们所见,在构造和析构过程中,有时需要初始化一些隐藏的成员变量。...1 * 合成并初始化虚函数表成员变量 2 * 执行析构函数体中,程序定义的其他析构代码 3 * 调用成员变量的析构函数(按照相反的顺序) 4 * 调用直接非虚基类的析构函数(按照相反的顺序) 5 * 如果是...“最终派生类”,调用虚基类的析构函数(按照相反顺序) 在VC++中,有虚基类的类的构造函数接受一个隐藏的“最终派生类标志”,标示虚基类是否需要初始化。...对于析构函数,VC++采用“分层析构模型”,代码中加入一个隐藏的析构函数,该函数被用于析构包含虚基类的类(对于“最终派生类”实例而言);代码中再加入另一个析构函数,析构不包含虚基类的类。...前一个析构函数调用后一个。 7 数组 堆上分配空间的数组使虚析构函数进一步复杂化。问题变复杂的原因有两个: 1、 堆上分配空间的数组,由于数组可大可小,所以,数组大小值应该和数组一起保存。
virtual函数是基类希望派生类重新定义的函数,希望派生类继承的函数不能为虚函数。根类一般要定义虚析构函数。 派生类只能通过派生类对象访问protected成员,不能用基类对象访问。...基类定义为virtual就一直为虚函数,派生类写不写virtual都是虚函数。用做基类的类必须是已定义的。 存在虚函数+指针或引用==产生多态。非虚函数编译时就按指针或引用或对象类型确定。...派生类继承基类的访问控制标号【何种方式继承】无论是什么,不影响派生类使用基类成员,但影响使用派生类的用户访问基类成员。类使用接口继承还是实现继承对派生类用户具有重要含义。 友元关系不继承。...派生类析构函数不负责清除基类成员,每个析构函数只负责清除自己成员。...派生类指针的静态类型和动态类型不一致时【基类指针指向派生类是时】,为保证删除指针调用合适的析构函数【多态】,基类析构必须为virtual。
派生类析构函数 析构函数不能被继承 执行派生类的析构函数时,基类的析构函数也将被调用 析构函数的执行顺序与构造函数严格相反 4.2.9 示例 ?...图4-8 派生类构造函数调用顺序 4.2.10 派生类构造函数使用中应注意的问题 派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有缺省的构造函数或者根本没有定义任何构造函数(编译器会自动生成缺省构造函数...图4-15 二义性示例 4.4 虚基类 格式:virtual 说明:关键字virtual与关键字public或private的相对位置无关,但必须位于虚基类名之前,且virtual...图4-16 虚基类与非虚基类存储结构 4.4.2 虚基类的构造函数 派生类中只有一个虚基类子对象 虚基类构造函数必须只被调用一次,目的是要保证虚基类子对象只被初始化一次 最派生类:继承结构中建立对象时所指定的类...虚基类子对象由最派生类的构造函数通过调用虚基类的构造函数进行初始化 在一个成员初始化列表中出现对虚基类和对非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数的执行 最派生类的构造函数的成员初始化列表中必须给出对虚基类的构造函数的调用
,BaseClass定义了虚析构函数、虚函数VirtualFunction1、虚函数VirtualFunction2、非虚函数NonVirtualFunction。...然后定义子类SubClass继承于BaseClass,覆盖重写BaseClass的虚析构函数、虚函数VirtualFunction1、函数NonVirtualFunction,未覆盖重写BaseClass...但是,subClass覆盖重写了baseClass的虚析构函数和VirtualFunction1,所以第一和第二个元素的值不同,指向的是SubClass对应的虚函数。...可见,调用虚函数和调用非虚函数有很大区别,调用虚函数时是在运行时期决定的,而调用非虚函数是在编译时期就决定了。...调用虚函数时会查询虚函数表对应函数的地址,如果子类覆盖重写基类的虚函数,子类的虚函数表对应函数的地址就会被编译器更新为子类的函数。
虚函数和虚表 编译器会为每一个包含虚函数的类(或通过继承得到的子类)生成一个表,其中包含指向类中每一个虚函数的指针。 这样的表就叫做虚表(vtable)。...1、非虚函数 hello()是类BaseClass中的非虚成员函数,不需要通过虚表查找,编译器直接生成调用语句call BaseClass::hello(),并且第一个参数默认为this指针。...堆分配的对象的析构函数在分配给对象的内存释放之前通过 delete 操作符调用。 其过程如下: 1、如果类拥有任何虚函数,则还原对象的虚表指针,使其指向相关类的虚表。...4、如果对象拥有一个超类,则调用超类的析构函数 5、如果是释放堆的对象,则用一个代理析构函数执行1~4步骤,并在最后调用delete操作符释放堆上的对象。...我们希望当调用C++基类BaseClass的析构函数时能够触发动态绑定,能够找到当前对象所属类的虚函数表中的析构函数。
而对于非虚函数的调用是在编译时进行绑定。类似的,通过对象进行的函数(虚函数或非虚函数)调用也在编译时绑定。 一旦某个函数被声明为虚函数,则在所有派生类中它都是虚函数。...析构函数的虚属性会被继承,无论派生类中使用合成的析构函数还是自定义的析构函数,都将是虚函数。这样,就能保证 delete基类指针时总能运行正确的析构函数版本。...假如基类析构函数不是虚函数,且指针的静态类型与动态类型不一致,则此时只能调用基类的析构函数,那派生类对象的部分则无法完成析构,从而产生未定义行为。...如前所述,当一个类中存在拷贝控制成员时,编译器不会为这个类合成移动操作。对于需要定义虚析构函数的基类,也是如此。...如果在基类中有一个不可访问和删除掉的析构函数,则派生类中合成的默认拷贝构造函数将是删除的,因为编译器无法销毁派生类对象的基类部分。 和过去一样,编译器将不会隐式合成一个删除掉的移动操作。
领取专属 10元无门槛券
手把手带您无忧上云