C++构造函数抛出异常需要注意的地方

从语法上来说,构造函数可以抛出异常。但从逻辑上和风险控制上,构造函数中尽量不要抛出异常。万不得已,一定要注意防止内存泄露。

1.构造函数抛出异常导致内存泄漏

在C++构造函数中,既需要分配内存,又需要抛出异常时要特别注意防止内存泄露的情况发生。因为在构造函数中抛出异常,在概念上将被视为该对象没有被成功构造,因此当前对象的析构函数就不会被调用。同时,由于构造函数本身也是一个函数,在函数体内抛出异常将导致当前函数运行结束,并释放已经构造的成员对象,包括其基类的成员,即执行直接基类和成员对象的析构函数。考察如下程序。

#include <iostream>
using namespace std;

class C
{
int m;
public:
C(){cout<<"in C constructor"<<endl;}
~C(){cout<<"in C destructor"<<endl;}
};

class A
{
public:
A(){cout<<"in A constructor"<<endl;}
~A(){cout<<"in A destructor"<<endl;}
};

class B:public A
{
public:
C c;
char* resource;

B()
{
resource=new char[100];
cout<<"in B constructor"<<endl;
throw -1;
}
~B()
{
cout<<"in B destructor"<<endl;
delete[] resource;
}
};

int main()
{
try
{
B b;
}
catch(int)
{
cout<<"catched"<<endl;
}
}

程序输出结果:

in A constructor
in C constructor
in B constructor
in C destructor
in A destructor
catched

从输出结果可以看出,在构造函数中抛出异常,当前对象的析构函数不会被调用,如果在构造函数中分配了内存,那么会造成内存泄露,所以要格外注意。

此外,在构造对象b的时候,先要执行其直接基类A的构造函数,再执行其成员对象c的构造函数,然后再进入类B的构造函数。由于在类B的构造函数中抛出了异常,而此异常并未在构造函数中被捕捉,所以导致类B的构造函数执行中断,对象b并未构造完成。在类B的构造函数“回滚”的过程中,c的析构函数和类A的析构函数相继被调用。最后,由于b并没有被成功构造,所以main()函数结束时,并不会调用b的析构函数,也就很容易造成内存泄露。

2.使用智能指针管理内存资源

使用RAII(Resource Acquisition is Initialization)技术可以避免内存泄漏。RAII即资源获取即初始化,也就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。智能指针是RAII最具代表的实现,使用智能指针,可以实现自动的内存管理,再也不需要担心忘记delete造成的内存泄漏。

因此,当构造函数不得已抛出异常时,可以利用“智能指针”unique_ptr来防止内存泄露。参考如下程序

#include <iostream>
using namespace std;

class A
{
public:
A() { cout << "in A constructor" << endl; }
~A() { cout << "in A destructor" << endl; }
};

class B
{
public:
unique_ptr<A> pA;
B():pA(new A)
{
cout << "in B constructor" << endl;
throw - 1;
}
~B()
{
cout << "in B destructor" << endl;
}
};

int main()
{
try
{
B b;
}
catch (int)
{
cout << "catched" << endl;
}
}

程序运行结果:

in A constructor
in B constructor
in A destructor
catched

从程序的运行结果来看,通过智能指针对内存资源的管理,尽管在类B构造函数抛出异常导致类B析构函数未被执行,但类A的析构函数仍然在对象pA生命周期结束时被调用,避免了资源泄漏。

以上就是C++构造函数抛出异常需要注意的地方的详细内容,更多关于C++构造函数的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++语法详解之封装、构造函数、析构函数

    大家先了解下什么是构造函数,什么是析构函数,作用是什么? 构造函数(方法)是对象创建完成后第一个被对象自动调用的方法.它存在于每个声明的类中,是一个特殊的成员方法.作用是执行一些初始化的任务.Php中使用__construct()声明构造方法,并且只能声明一个. 析构函数(方法)作用和构造方法正好相反,是对象被销毁之前最后一个被对象自动调用的方法.是PHP5中新添加的内容作用是用于实现在销毁一个对象之前执行一些特定的操作,诸如关闭文件和释放内存等. 下面在通过具体例子看下C++语法详解之封装.构

  • 详解C++ 拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象.拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象. 复制对象把它作为参数传递给函数. 复制对象,并从函数返回这个对象. 如果在类中没有定义拷贝构造函数,编译器会自行定义一个.如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数.拷贝构造函数的最常见形式如下: classname (const classname &obj) { // 构造函数的主体 } 在这里,o

  • 详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存.(构造函数的命名必须和类名完全相同) 首先说一下一个C++的空类,编译器会加入哪些默认的成员函数 默认构造函数和拷贝构造函数 析构函数 赋值函数(赋值运算符) 取值函数 **即使程序没定义任何成员,编译器也会插入以上的函数! 注意:构造函数可以被重载

  • C++构造函数和析构函数的使用与讲解

    构造函数(constructor) 1.构造函数是种特殊的类成员函数,遵循如下规则: a.函数名与类名必须相同. b.没有返回值 例如: class Obj { ... public: Obj() { ... } }; 2.构造函数可以带参数,也可以重载 class Obj { ... public: Obj() { ... } Obj(int x, int y) { ... } }; 3.构造函数和普通成员函数不一样,一般不显示调用.在创建一个对象时,构造函数自动调用(编译器来完成). 析构函

  • C++类继承之子类调用父类的构造函数的实例详解

    C++类继承之子类调用父类的构造函数的实例详解 父类HttpUtil: #pragma once #include <windows.h> #include <string> using namespace std; class HttpUtil { private: LPVOID hInternet; LPVOID hConnect; LPVOID hRequest; protected: wchar_t * mHostName; short mPort; string send

  • 详解C++ 编写String 的构造函数、拷贝构造函数、析构函数和赋值函数

    详解C++ 编写String 的构造函数.拷贝构造函数.析构函数和赋值函数 编写类String 的构造函数.析构函数和赋值函数,已知类String 的原型为: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~ String(void); // 析构函数 String & operate =(const String &ot

  • C++ 中构造函数的实例详解

    C++ 中构造函数的实例详解 c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初学者有所帮助. 1. 构造函数是干什么的 class Counter { public: // 类Counter的构造函数 // 特点:以类名作为函数名,无返回类型 Counter() { m_value = 0; } private: // 数据成员 int m_va

  • c++ 子类构造函数初始化及父类构造初始化的使用

    我们知道,构造方法是用来初始化类对象的.如果在类中没有显式地声明构造函数,那么编译器会自动创建一个默认的构造函数:并且这个默认的构造函数仅仅在没有显式地声明构造函数的情况下才会被创建创建. 构造函数与父类的其它成员(成员变量和成员方法)不同,它不能被子类继承.因此,在创建子类对象时,为了初始化从父类中继承来的成员变量,编译器需要调用其父类的构造函数.如果子类的构造函数没有显示地调用父类的构造函数,则默认调用父类的无参构造函数,至于什么事显式调用,在下面会详细说明!关于子类中构造函数的构造原则,总

  • C++中的移动构造函数及move语句示例详解

    前言 本文主要给大家介绍了关于C++中移动构造函数及move语句的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先看一个小例子: #include <iostream> #include <cstring> #include <cstdlib> #include <vector> using namespace std; int main() { string st = "I love xing"; vec

  • 关于C++复制构造函数的实现讲解

    复制构造函数是一种特殊的构造函数,有一般构造函数的特性.它的功能是用一个已知的对象来初始化一个被创建的同类对象.复制构造函数的参数传递方式必须按引用来进行传递,请看实例: #include <iostream> #include <cstring> using namespace std ; class Student { private : char name[8]; int age ; char sex ; int score ; public : void disp(); /

  • C++中构造函数的参数缺省的详解

    C++中构造函数的参数缺省的详解 前言: 构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值.在构造函数中也可以采用这样的方法来实现初始化. #include <iostream> using namespace std; class A { public : A(int aa=0,int bb=00); //在声明构造函数时指定默认参数 int volume( ); int a; int b; }; int main( ) { A

随机推荐