析构函数

c++析构函数、虚析构函数、纯虚析构函数详解

倖福魔咒の 提交于 2019-12-07 08:21:22
我们知道对象在结束其生命周期之前,都会调用析构函数以完成必要的清理工作;派生类调用的析构函数顺序是 “先子类,后基类” ; 这篇文章用于总结 当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用delete运算符删除一个指针对象时,析构函数会有什么情况发生 ; #### 普通析构函数 #### CBase是基类,CDerive是其子类,类源码代码如下: class CBase { public: CBase(){} //基类析构函数 ~CBase(){ cout << "CBase Destructor" << endl; } private: int a; }; class CDerive:public CBase { public: CDerive(){} //子类析构函数 ~CDerive(){ cout << "CDerive Destructor" << endl; } private: int b; }; 测试代码如下: //case1 CDerive *pDeriveObj = new CDerive(); delete pDeriveObj; //case2 //基类指针对象可以指向派生类,体现基类和派生类赋值兼容关系(不同类型可以转化和赋值), //但是pBaseObj只能访问基类成员,不能访问派生类成员 CBase* pBaseObj = new

C++析构函数

 ̄綄美尐妖づ 提交于 2019-12-07 08:21:03
什么是析构函数 析构函数是类的一个成员函数,名字由波浪号接类名构成,没有返回值,不接受参数,一个类只有一个析构函数。当一个类未定义自己的析构函数时,编译器会为它定义一个合成的析构函数。 析构函数完成什么工作 在构造函数中,成员的初始化时在函数体执行前完成的,且按照他们在类中的顺序进行初始化。在析构函数中,首先执行函数体,然后按照初始化顺序的逆序销毁成员。 析构函数销毁类类型成员时执行成员自己的析构函数,内置类型(包括指针)则什么也不做,智能指针是类类型,会被自动销毁。 析构函数什么时候被调用 变量在离开其作用域时被销毁。 当一个对象被销毁时,其成员被销毁。 容易起被销毁时,其元素也被销毁。 对于动态分配的对象,当对指向它的指针使用delete运算符时被销毁。 对于临时对象,创建它的完整表达式结束时被销毁。 派生类在销毁时,派生类的析构函数首先执行,然后是基类的析构函数,以此类推,沿着继承体系的反方向直至最后。 虚析构函数 在执行析构函数时,如果指针指向的对象的静态类型与动态类型不符,例如一个指向基类对象的指针实际上指向了一个派生类对象,那么只有基类的析构函数会被执行,为了确保执行正确的析构函数,可以将基类中的析构函数声明为虚函数,一个基类总是需要虚析构函数。虚析构函数将阻止合成移动操作。 class Person { public : const char * name_ ;

析构函数的作用及合成析构函数

こ雲淡風輕ζ 提交于 2019-12-07 08:20:34
析构函数 用于释放在构造函数或在对象生命期内获取的资源。 如果类需要析构函数 ,那么也需要复制操作符和复制构造函数。 何时调用析构函数? 当对象的引用或指针超出作用域时,不会运行析构函数,只有删除指向动态分配对象的指针或实际对象(而不是对象的引用)超出作用域时,才会运行析构函数。 合成析构函数: 编译器总是为我们合成一个析构函数,合成析构函数按对象创建时的逆序撤销每个非static成员,对于类类型的成员,合成析构函数调用该成员的析构函数来撤销对象。 析构函数是在类名字前加~,没有返回值,没有形参,不能重载析构函数,我们可以为一个类定义多个构造函数,但只能提供一个析构函数 析构函数和构造函数的一个不同是,即使你定义了析构函数,合成析构函数也会运行。 来源: CSDN 作者: richard_如风 链接: https://blog.csdn.net/ycxmartin111111/article/details/17035821

[C++] Virtual Destructor

做~自己de王妃 提交于 2019-12-07 08:20:17
假定有如下的两个类: class Base { // some virtual methods }; class Derived : public Base { ~Derived() { // Do some important cleanup } }; 然后有下面的语句: Base *b = new Derived(); delete b; // Here's the problem! 行为未定义!!! 解决办法是将 Base 的析构函数定义为 virtual 。 当删除指针时,如果指针的对象的静态类型不同于它的动态类型,那么:静态类型必须为基类,且基类必须定义 virtual destructor,否则行为就是未定义的。 而且,如果没有将基类的析构函数定义为 virtual, 当delete指针时,只有基类的析构函数被调用,派生类的析构函数不会被调用,这会导致资源泄漏,总而言之,如果将基类用于多态,那么,基类的析构函数应该总是定义 virtual destructor。 [Effective C++] 假定有如下表示敌方目标的类,静态变量用于记录对象数目: class EnemyTarget { public: EnemyTarget() { ++numTargets; } EnemyTarget(const EnemyTarget&) { ++numTargets; }

Effective C++学习笔记一

ε祈祈猫儿з 提交于 2019-12-07 08:18:00
条款 01 : 视C++ 为一个语言联邦 Ø C Ø Object-Oriented C++ Ø Template C++//泛型编程部分( generic programming ) Ø STL C++ 高效编程守则视情况而变化,取决与你使用 C++ 的 哪一部分。 条款 02 : 尽量以 const, enum , inline替换#define Ø 对于单纯常量,最好以 const 对象或者 enums 替换#defines 。 Ø 对于形似函数的宏,最好改用 inline 函数替换 #defines 。 条款 03 : 尽可能使用 const Ø 声明为 const 可帮助编译器侦测出错误用法。 Ø 编译器强制实施 bitwise constness, 但你编写程序时应该使用 “ 概念 上的常量性 ” 。 Ø 当const 和 non-const 成员函数有 着实质等价的实现时,令 non-const 版本调用 const 版本 可避免代码重复。 条款 04 : 确定对象被使用前已先被初始化 Ø C++ 成员初始化次序是固定的 ,class 的成员变量总是以其声明次序被初始化。但是 它们可以在成员初值列中以不同的次序出现,没有影响。 Ø 构造函数中注意分清初始化操作和赋值操作。初始化是在成员初始化列表里面,而赋值是在构造函数本体里面进行的。 Ø 构造函数最好使用成员初值列

Effective C++学习笔记

非 Y 不嫁゛ 提交于 2019-12-07 08:14:16
条款1:视C++为一个语言联邦 C++有4大组件:C、Object-OrientedC++、Template C++、STL。 条款2:尽量以const、enum、inline替换#define 对于单纯常量,最好以const对象或enums替换#define。 对于形似函数的宏,最好改用inline函数替换#defines。 条款3:尽可能用const 将某些东西声明为const可帮助编译器侦测出错误用法,const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。 当const和non-const成员函数有着实质等价的代码时,令non-const版本调用const版本可避免代码重复。 条款4:确定对象被使用前已被初始化 为内置对象进行手工初始化,因为C++不保证初始化它们。 构造函数最好使用成员初始化列表(memberinitialization list),而不要在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,其排列次序应该和它们在类中的声明次序相同。 为避免“跨编译单元之初始化次序”问题,请以localstatic对象替代non-local static对象。 条款5:了解C++默默编写并调用了那些函数 编译器可以暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数。

Effective C++ —— 别让异常逃离析构函数

浪尽此生 提交于 2019-12-07 08:13:57
C++ 并不禁止析构函数吐出异常,但不鼓励这么做。 1.如果一个被析构函数调用的函数可能抛出异常,则析构函数应该捕获这些异常,然后终止程序或吞下它们。 2.如果客户需要对某个函数在运行中抛出的异常做出反应,那么这个类应该提供一个普通函数(非析构函数)执行该操作。 class Widget { public : Widget(); ~Widget() {} }; void work() { std :: vector <Widget> v; } v在销毁时有责任销毁容器内的所有Widget 对象,如果在销毁第一个对象时抛出了异常,剩下的对象还是应该被销毁。如果第二个对象被销毁时又出现了异常,这时对于C++而言异常有些多了,程序不是结束执行就是导致不明确行为。 使用标准库容器或数组等都有可能出现这样的情况,其实只要析构函数吐出异常,程序都会出现这样的问题。 第二个例子:DBConnection 类负责数据库连接 class DBConnection { public: static DBConnection Create(); private: void close();//关闭联机,释放资源,失败则抛出异常 }; 为确保客户不忘记调用close()方法来释放资源,可以设计一个数据库资源管理类,在其析构函数中调用close(). class DBResManager { public:

禁止让异常逃离析构函数

女生的网名这么多〃 提交于 2019-12-07 08:13:27
禁止让异常逃离析构函数 1.说明 当对象在其生命周期结束时(例如对象所在的函数已调用完毕)或抛出异常(前提是对象构造成功,即构造函数无异常),系统会自动调用析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。 2.析构函数发生异常的危害 如果析构函数发生异常,那么将导致,发生异常之后的代码无法执行,也就会导致有些资源没有释放,造成资源泄露。 3.防止异常逃离析构函数 下述代码:进行数据库连接和关闭的管理,当close出现异常,异常会逃离析构函数,后面的资源没有释放,这种方式容易导致资源泄露 class DBCon { public: ... ~DBCon() { m_db.close(); //关闭数据库连接 } private: DBConnection m_db; } 方法一:异常捕获机制,根据实际需要,吞下异常(后面能够继续执行,资源自然能够释放) 或者中断程序(整个进程终止,内存回归系统,资源自然能够释放), 以保证资源的正常释放 DBCon::~DBCon() { try { m_db.close(); //关闭数据库连接 } catch(...) { //吞下异常,以保证后面资源能继续释放 ... //或者abort()中断程序,防止不明确行为 } } 方法二

别让异常逃离析构函数

℡╲_俬逩灬. 提交于 2019-12-07 08:12:33
C++并不禁止析构函数吐出异常,但它不鼓励这样做。如: class Widget { public: ... ~Widget(){...} //这个可能会出现异常 }; void doSomething() { vector<Widget> v ... //v在这里被自动销毁 } 当vector v被销毁,它有责任销毁其内含的所有Widgets,假设v内含十个Widgets,而在析构第一个元素期间,有个异常被抛出。其他九个Widgets还是应该被销毁(否则它们保存的任何资源都会发生泄漏),因此v应该调用它们各个析构函数。但假设在那些调用期间,第二个Widget析构函数又抛出异常。现在有两个同时作用的异常,这对C++而言太多了。在两个异常同时存在的情况下,程序若不是结束执行就是导致不明确的行为。所以,C++不喜欢析构函数出现异常。 但如果你的析构函数必须执行一个动作,而该动作可能会在失败时抛出异常,该这么办?如,假设你使用一个class负责数据库的连接: class DBConnection { public: ... static DBConnection create(); void close(); //关闭联机,失败则抛出异常 }; 为了确保客户不会忘记在DBConnection对象身上调用close()

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

混江龙づ霸主 提交于 2019-12-07 08:03:02
Effective C++中说过: 1、带有多态性质的base class应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数; 2、Classes的设计目的如果不是作为base class使用, 或者不是为了具有多态性质,就不该声明virtual 析构函数。 #ifndef __EFFECTIVE_H__ #define __EFFECTIVE_H__ /*include*/ #include <iostream> using namespace :: std; class baseClass { public : baseClass() { cout << "base Class construction function" << endl; } ~baseClass() { cout << "base Class destruction function" << endl; } }; class derivedClass : public baseClass { public : derivedClass() { cout << "derived Class construction function" << endl; } ~derivedClass() { cout << "derived Class