先说构造函数,构造函数作为虚函数是不可以的,首先c++编译器上不会让你通过 在内存上,我们知道,一个对象会有一个虚函数表,虚函数表在构造函数中初始化,可是一个对象还没有完成实例化,他的虚函数表是不存在的...,一个对象需要调用构造函数完成实例化,这里形成了一个悖论 在意义上,将构造函数声明为虚函数没有意义,虚函数主要是实现多态,c++的多态是在运行时构建基类基类来调用不同函数,而不是根据情况动态调用构造函数...构造函数是不行的,但是析构函数作为虚函数确实常用的,特别是基类的析构函数一定要声明为虚函数。首先既然对象存在,那么虚函数表肯定存在,所以析构函数作为虚函数是合理的。...这时候如果是基类指针指向子类对象,那么删除指针,只会调用基类的析构函数,因为这时候对象类型是基类对象,析构函数没有动态绑定,只会调用当前对象类型的析构。...但是如果将基类析构函数声明为虚函数,则能成功调用子类的析构函数 #include using namespace std; class Father { public:
结论:当父类存在virtual函数时,则需要实现虚析构函数。...\n"); } //virtual ~AAA() { printf("AAA 析构 ......delete pp; pp = nullptr; } } int main() { test(); return 0; } 如上,假如AAA不实现虚析构函数...,则通过new出来的对象,被析构时,会调用不到子类的析构。...父类声明了虚析构,则能够正确调用子类的析构。
开始学C++了,所以又重拾以前学习过的相关概念… 析构函数是当一个对象的生命周期结束时,会自动执行析构函数。...当一个类不准备作为基类使用时,就不要定义虚析构函数了,因为它会增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移值性。 所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。...当且仅当类里包含至少一个虚函数的时候,才去声明虚析构函数。 抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以在想要成为抽象类的类里声明一个纯虚析构函数。...… B::f() A::ff() //定义指向基类对象的指针a,当调用f()方法时,因为f为虚函数,所以调用了派生类的f(),输出B::f(); 参考: 详解C++中的纯虚函数(虚函数区别)&多态性以及实例应用...C++析构函数、构造函数、虚函数关系 C++中虚函数工作原理和(虚)继承类的内存占用大小计算
当派生类对象从内存中撤销时一般先运行派生类的析构函数,然后再调用基类的析构函数。...解决的方法是将基类及派生类的析构函数设为虚函数,这时无论基类指针指向哪个派生类对象,系统会采用动态关联,调用相应的析构函数对对象进行清理。...下面将基类的析构函数改成虚析构函数 virtual ~Point(){ std::cout << "Point destructor" << std::endl; } 其它的不变,再运行: ?...这样就达到我们的目的了,基类,派生类都调用了析构函数,另外需要注意的是 在基类的析构函数声明为虚函数时,由该基类派生的析构函数也自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。 ...程序中显示的用delete运算符删除一个对象,而这个对象是指向派生类对象的基类指针,系统调用相应派生类的析构函数。
A 的构造函数 , 然后调用 B 的构造函数 , 最后调用 C 的构造函数 ; 参考 【C++】继承 ⑧ ( 继承 + 组合 模式的类对象 构造函数 和 析构函数 调用规则 ) 博客中 , 构造函数...; 自身定义的 析构函数 ; 然后 , 调用 成员 析构函数 ; 也就是 成员变量 类型的 析构函数 ; 最后 , 调用 父类 析构函数 ; 2、析构函数可以是虚函数 析构函数 可以是 虚函数 ; 虚析构函数...virtual ~Base() {} }; 4、虚析构函数意义 父类中使用了 虚析构函数 , 在 子类 中 , 必须 覆盖 父类 的虚析构函数 , 并且使用相同的函数签名 ; 如果 子类 没有提供自己的..., 只有在 父类 的析构函数是 虚函数 时 , 子类 的析构函数才必须是虚函数 ; 如果 父类 的 析构函数 不是 虚函数 , 则 子类 的 析构函数 可以是 普通的 非虚函数 ; 二、代码示例 -...虚析构函数 1、代码示例 - 没有使用虚析构函数导致子类析构函数无法调用 在下面的代码中 , 声明 子类指针 指向 子类对象 , 释放 子类指针 时 先调用 子类析构函数 , 再调用父类析构函数 ; 声明
第七步:执行类A的析构函数,输出"析构函数A" ~A() { cout << "析构函数A" << endl; } virtual void fund() {...cout << "清除A" << endl; } }; class B : public A { public: // 第二步:执行类B的构造函数,调用类A里虚函数,输出"构造A"...,并调用func(); // 由于fun()不是构造函数和析构函数,且func()为虚函数 // 所以最终结果输出"开始...类C" void fun() {..."; func(); } // 第六步:执行类B的析构函数,调用fund()函数; // 由于是在析构函数里,且fund()为虚函数,所以执行类A里的fund()...} // 第五步:执行类C的析构函数,输出"清除C" ~C() { fund(); } void fund() { cout << "清除C" <
为什么析构函数要声明成virtual呢? 因为,如果delete一个基类的指针时, 如果它指向的是一个子类的对象,那么析构函数不为虚就会导致无法调用子类析构函数,从而导致资源泄露。...如果把virtual属性去掉,那么被调用的是~Animal(),Dog类的构造函数被调用而析构函数未被调用,构造函数中分配的资源没有释放,从而产生了内存泄漏。...如果基类析构函数不加virtual,运行效果如下: Animal ctor Dog ctor Dog::foo Animal dtor 可另一个问题是,有时virtual是不需要的。...去掉析构函数的virtual属性后,因为该类中没有其他的virtual函数,所以编译时不会生成v-table,这样就节省了编译时间,并减少了最终生成的程序的大小。...如果是,则调用: delete this; 因为Release()是virtual的,所以该COM对象对应的正确的派生类被调用,delete this会调用正确的析构函数,达到了使用virtual析构函数的效果
C.127: A class with a virtual function should have a virtual or protected destructor C.127:包含虚函数的类应该有虚析构函数或保护析构函数...包含虚函数的类通常(大多数情况下)通过指向基类的指针使用。通常,最后一个使用者必须通过指向基类的指针调用delete操作,通常是指向基类的智能指针,因此析构函数应该是公开的虚函数。...稍微特殊一些的情况是:如果不希望支持通过指向基类的指针销毁对象,析构函数应该是保护的非虚函数。参见C.35。...包含虚函数的类的析构函数要么是公开的虚函数,要么是保护的非虚函数。...提示针对包含虚函数却没有虚析构函数的类的销毁操作。
二、虚析构函数 使用方法和规则与虚函数一样 格式要求: 虚析构函数要求基类与派生类中的名称不一致 只要基类的析构函数是虚函数,就能确保我们在释放指针时准确的运行哪个版本(基类or派生类)的析构函数 如果基类指针指向于自己...,那么delete的时候执行的就是自己的析构函数 如果基类指针指向于派生类对象,那么delete的时候执行的就是派生类的析构函数(这个就是多态的性质,与执行虚函数的原理一样) 如果基类的析构函数不是虚函数...,则delete一个指向派生类对象的基类指针将产生未定义的行为 与虚函数一样,如果基类的析构函数为virtual,那么派生类的析构函数也都是virtual的(即使是编译器默认合成的也是virtual的)...但是基类的虚析构函数并不遵循这个规则:一个基类总是需要析构函数,而且它能将析构函数设定为虚函数,此时,该析构函数为了成为虚函数而令内容为空,我们显然无法由此推断该基类还释放需要复制运算符或拷贝构造函数...②虚析构函数将阻止合成移动操作:基类需要一个虚析构函数这一事实还会对基类和派生类的定义产生另外一个间接的影响:如果一个类定义了析构函数,即使它通过default的形式使用了合成的版本,编译器也不会为这个类合成一定操作
3、继承抽象类的意义就是提供族类的公共接口。 4、子类继承的纯虚函数,如果未实现,子类仍然为抽象类,仍然不能被实例化。 【虚函数的若干限制】 1、只有类的成员函数才能声明为虚函数。...虚函数仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数。 2、静态成员函数不能是虚函数静态成员函数不受对象的捆绑,只有类的信息。 3、内联函数不能是虚函数。...4、构造函数不能是虚函数,构造时,对象的创建尚未完成。构造完成后,能算一个名符其实的对象。 5、析构函数可以是虚函数且通常声明为虚函数。...(2, 4, 8); s->draw(); // delete 指针,调用析构函数 delete s; return 0; } 以上代码演示了纯虚函数的定义,但上面代码存在一个问题,我们先看一下运行的结果...,初始化为 0,提供族类的公共接口 virtual void draw() = 0; // 增加 virtual 关键字,让其自动执行子类析构函数 virtual ~Shape() { cout <
在Visual C++中,包含虚函数的类对象的虚指针被安排在对象的起始地址处,并且虚函数表(vtable)的地址是由构造函数写入虚指针的。...2.不要在析构函数中调用虚函数的原因 同样的,在析构函数中调用虚函数,函数的入口地址也是在编译时静态决定的。也就是说,实现的是实调用而非虚调用。 考察如下例子。...的对象b退出作用域时,会先调用类B的析构函数,然后调用类A的析构函数,在析构函数~A()中,调用了虚函数show()。...从输出结果来看,类A的析构函数对show()调用并没有发生虚调用。...从概念上说,析构函数是用来销毁一个对象的,在销毁一个对象时,先调用该对象所属类的析构函数,然后再调用其基类的析构函数,所以,在调用基类的析构函数时,派生类对象的“善后”工作已经完成了,这个时候再调用在派生类中定义的函数版本已经没有意义了
构造函数 对象创建的时候执行 student s //空参构造函数 栈内存中 student s("测试")//带参构造函数 栈内存中 或者 student *s=new student//空参构造函数...堆内存中 student *s=new student("测试")//带参构造函数 堆内存中 析构函数 对象销毁的时候执行 delete s 在构造函数中分配的堆内存空间需要在析构函数中进行释放 ?...带参构造函数变量重名问题 使用关键字this解决 ?
C++中每个类都有其构造与析构函数,它们负责对象的创建和对象的清理和回收,即使我们不写这两个,编译器也会默认为我们提供这些构造函数。...下面仍然是通过反汇编的方式来说明C++中构造和析构函数是如何工作的。...编译器是否真的会默认提供构造与析构函数 在一般讲解C++的书籍中都会提及到当我们不为类提供任何构造与析构函数时编译器会默认提供这样六种成员函数:不带参构造,拷贝构造,“=”的重载函数,析构函数,以及带const...+代码与之前的仅仅是多了一个虚函数,这个时候编译器为这个类定义了一个默认的构造函数,从汇编代码中可以看到,这个构造函数主要初始化了类对象的头4个字节,将虚函数表的地址放入到这个4个字节中,因此我们得出结论...,但是接着执行类的析构函数析构在函数中定义的类对象,接受返回值得这块内存一直等到它所在的语句块结束才调用析构 如果不要这个返回值时又如何呢,下面的代码说明了这个问题 int main() {
构造函数不能是虚函数,析构函数可以是虚函数 1、抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用 2、可使用指向抽象类的指针支持运行时多态性 3、派生类中必须实现基类中的纯虚函数,否则它仍将被看作一个抽象类...,并能管理该对象 可以添加新类(已有类的派生类)的新对象,并能管理该对象 四、虚析构函数 析构函数可以声明为虚函数 delete 基类指针; 程序会根据基类指针指向的对象的类型确定要调用的析构函数...基类的析构函数为虚函数,所有派生类的析构函数都是虚函数 构造函数不得是虚函数 如果要操作具有继承关系的类的动态对象,最好使用虚析构函数。...特别是在派生类析构函数需要完成一些有意义的操作,比如释放内存 析构函数还可以是纯虚的。...如果想要将它定义成抽象类,只能将虚析构函数声明为纯虚的 // 通常情况下在基类中纯虚函数不需要实现 // 例外是纯虚析构函数要给出实现。
在Visual C++中,包含虚函数的类对象的虚指针被安排在对象的起始地址处,并且虚函数表(vtable)的地址是由构造函数写入虚指针的。...同样的,在析构函数中调用虚函数,函数的入口地址也是在编译时静态决定的。也就是说,实现的是实调用而非虚调用。 考察如下例子。...的对象b退出作用域时,会先调用类B的析构函数,然后调用类A的析构函数,在析构函数~A()中,调用了虚函数show()。...从输出结果来看,类A的析构函数对show()调用并没有发生虚调用。...从概念上说,析构函数是用来销毁一个对象的,在销毁一个对象时,先调用该对象所属类的析构函数,然后再调用其基类的析构函数,所以,在调用基类的析构函数时,派生类对象的“善后”工作已经完成了,这个时候再调用在派生类中定义的函数版本已经没有意义了
一、构造函数和析构函数的由来 类的数据成员不能在类的声明时候初始化,为了解决这个问题? 使用构造函数处理对对象的初始化。...二、类的构造函数 (1)初识类的初始化 C++支持两种初始化形式:复制初始化和直接初始化,对于类直接初始化直接调用实参匹配的构造函数,复制初始化总是调用复制构造函数。...三、类的析构函数 类的析构函数和构造函数作用相反,释放对象使用的资源,并销毁非static成员。 (1)内存泄漏 下面代码有何隐患?...其实类也是一样的,上述代码就会有内存泄漏的风险。如何解决呢?看下述代码。 在上述代码中,我们在析构函数中,添加delete函数。解决了内存泄漏的问题,但是还存在其他问题。...4、当析构函数中存在手动释放资源的时侯,一定要注意之前是否释放过,以及以后是否有其他操作会释放。
这个要提一下,如果记不住就记住:如果不做虚析构函数,会有内存泄漏 解释 定义一个基类的指针p,在delete p时,如果基类的析构函数是虚函数,这时只会看p所赋值的对象,如果p赋值的对象是派生类的对象,...就会调用派生类的析构函数;如果p赋值的对象是基类的对象,就会调用基类的析构函数,这样就不会造成内存泄露。...如果基类的析构函数不是虚函数,在delete p时,调用析构函数时,只会看指针的数据类型,而不会去看赋值的对象,这样就会造成内存泄露。 多少学点设计模式就清楚了。...接下来是一个子类 class Inherit :public Base{ //此处省去,一切从简 }; //重点看调用 int main() { Base *p = new Inherit; //这种方式的调用...,这时候有没有虚析构就不一样了 delete p; Base *q = new Base; delete q; return 0; }
目录 前言 1.类的6个默认成员函数 2.构造函数 2.1概念 2.2构造函数特性 3.析构函数 3.1概念 3.2析构函数特性 4.结语 1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类...例如: class Date {}; 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。...3.2析构函数特性 析构函数是特殊的成员函数,其特征如下: 析构函数名是在类名前加上字符 ~。 无参数无返回值类型。 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。...注意:析构函数不能重载。 对象生命周期结束时,C++编译系统系统自动调用析构函数。...而Date没有显式提供析构函数,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
C.82: Don't call virtual functions in constructors and destructors C.82:不要在构造函数或析构函数中调用虚函数 Reason...到目前为止,被调用的函数应该只属于构造对象本身,而不是可能存在于派生类中的某个覆盖函数。那样做非常难理解。...最坏的情况,在构造函数或者析构函数中直接或间接调用一个没有实现的纯虚函数会导致没有定义的行为。...从构造函数和析构函数中调用虚函数并不是本身有什么错误。这种调用的语义是安全的。然而,经验表明这样的调用很少是必须的,很容易扰乱维护者,如果被新手使用会成为错误源。...提示来自构造函数或析构函数的虚函数调用。
构造函数是一种特殊的成员函数,它在创建对象时自动调用,其主要目的是初始化对象。在C++中,构造函数具有与其所属类相同的名称,并且没有返回类型。...Time类的对象,为什么最后会调用Time类的析构函数?...d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数 但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数...,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁 main函数中并没有直接调用...Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数
领取专属 10元无门槛券
手把手带您无忧上云