构造函数抛出异常

试分析推断下述代码的输出结果:

#include <stdio.h>
#include <stdlib.h>
#include <exception>
using namespace std;

void* operator new(size_t size)
{
	printf("my new -> %u\n", size);
	return malloc(size);
}

void operator delete(void *p)
{
	printf("mydelete\r\n");
	return free(p);
}

class A
{
public:
	A()
	{
		printf("A\n");
		throw int(-1);
	}
	~A()
	{
		printf("~A\n");
	}
};

int main()
{
	A* p = NULL;
	try
	{
		p = new A;
	}
	catch (int& i)
	{
		printf("exception -> %d\n", i);
	}
	return 0;
}

要知道上述代码的输出结果,可能需要知道C++的new到底做了什么,以及如果析构函数抛出异常的话,C++是如何处理这种情况的。

实际上,上述代码的输出结果是:

my new -> 1
A
mydelete
exception -> -1

实际上,当new一个对象时,C++首先需要分配对象的内存,然后才会调用对象的析构函数,而且C++会调用new(sizeof(T))函数分配内存。其大致过程如下所示:

//new 实际过程大致是是:
A* p = (A*)malloc(sizeof(A));
try
{
	A();
}
catch(...)
{
	// 保证异常后不会造成内存泄露
	// 不会调用析构函数(对象都没创建成功,析构函数没意义了)
	delete p;
	throw exception;
}

此外,当new过程中发现构造函数抛出异常而且构造函数没能处理该异常的话,那么C++就会使得该对象的创建失败,同时会释放已经分配好的内存,但是需要注意的是,此时C++不会去掉用对象的析构函数。

所以由上述知识我们就可以具体分析上述代码的输出结果了。

因此,对于构造函数抛出异常的情况,我们可做下述分析:

#include <stdio.h>
#include <stdlib.h>
#include <exception>
using namespace std;

// 重载new
void* operator new(size_t size)
{
	printf("my new -> %u\n", size);
	return malloc(size);
}

// 重载delete
void operator delete(void *p)
{
	printf("mydelete\r\n");
	return free(p);
}

class A
{
public:
	A()
	{
		printf("A\n");
		// 析构函数抛出异常
		// C++自动会释放已分配的内存但是不会调用析构函数
		throw int(-1);
	}
	~A()
	{
		printf("~A\n");
		// 析构函数不推荐抛出异常,如果一定要,要自我实现异常处理
	}
};

int main()
{
	A* p = NULL;
	try
	{
		// 此处实际调用 operator new (sizeof(A))
		// 如果构造函数抛出了异常new的返回值为NULL
		p = new A;
	}
	catch (int& i)
	{
		printf("exception -> %d\n", i);
	}
	return 0;
}
时间: 2024-11-08 18:57:51

构造函数抛出异常的相关文章

C++关于构造函数 和 析构函数 能否抛出异常的讨论

构造函数和析构函数分别管理对象的建立和释放,负责对象的诞生和死亡的过程.当一个对象诞生时,构造函数负责创建并初始化对象的内部环境,包括分配内存.创建内部对象和打开相关的外部资源,等等.而当对象死亡时,析构函数负责关闭资源.释放内部的对象和已分配的内存. 在对象生死攸关的地方,如果程序代码出现问题,常常会发生内存泄漏,从而产生可能危害系统运行的孤魂野鬼.大量的事实表明,业务逻辑代码写得非常严谨的程序在运行中仍然发现存在内存泄露,大都是构造和析构部分的代码存在问题. 而许多程序员都习惯于面向对象的编

构造函数失败_抛出异常

网上比较经典的总结: 什么函数都有可能失败,构造函数也不另外,比如new一个对象或空间不成功.当构造函数失败的时候,其实很多时候我们不想这个对象被继续生成,这个时候就可以在构造函数里面抛出异常.C++规定构造函数抛出异常之后,对象将不被创建,析构函数也不会被执行,但已经创建成功的部分(比如一个类成员变量)会被部分逆序析构,不会产生内存泄漏.但有些资源需要在抛出异常前自己清理掉,比如打开成功的一个文件,最好关闭掉再抛出异常(虽然系统也会把这个资源回收),因为抛出异常之后析构函数不会被执行了. (1

构造函数与虚构函数

1.构造函数和析构函数为什么没有返回值? 构造函数和析构函数是两个非常特殊的函数:它们没有返回值.这与返回值为void的函数显然不同,后者虽然也不返回任何值,但还可以让它做点别的事情,而构造函数和析构函数则不允许. 在程序中创建和消除一个对象的行为非常特殊,就像出生和死亡,而且总是由编译器来调用这些函数以确保它们被执行.如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由客户程序员自己来显式的调用构造函数与析构函数,这样一来,安全性就被破坏了.另外,析构函数不带任何参数,因为析构不需

构造函数和析构函数中得异常处理

一. 构造函数 总结如下: 1. 构造函数中抛出异常,会导致析构函数不能被调用,但对象本身已申请到的内存资源会被系统释放(已申请到资源的内部成员变量会被系统依次逆序调用其析构函数). 2. 因为析构函数不能被调用,所以可能会造成内存泄露或系统资源未被释放. 3. 构造函数中可以抛出异常,但必须保证在构造函数抛出异常之前,把系统资源释放掉,防止内存泄露.(如何保证???使用auto_ptr???) 试验代码: 1 //ExceptionConstructor.h 2 #pragma once 3

C++构造函数异常(二)

继续上一篇文章提到的构造异常话题, 第三个场景:对继承中,某个基类构造异常,而其他基类已构造成功,则构造成功的基类不会析构,由编译器负责回收 1 class B{ 2 3 public: 4 B(){ 5 age = 0; 6 cout << "construct B default" << endl; 7 throw 0;//抛出异常 8 } 9 10 ~B(){ 11 cout << "destructor B ,age="

More Effective C++ 条款10 在构造函数内阻止内存泄露

1. “C++ 只会析构已完成的对象”,“面对未完成的对象,C++ 拒绝调用其析构函数”,因为对于一个尚未构造完成的对象,构造函数不知道对象已经被构造到何种程度,也就无法析构.当然,并非不能采取某种机制使对象的数据成员附带某种指示,“指示constructor进行到何种程度,那么destructor就可以检查这些数据并(或许能够)理解应该如何应对.但这种机制无疑会降低constructor的效率,,处于效率与程序行为的取舍,C++ 并没有使用这种机制.所以说,”C++ 不自动清理那些’构造期间跑

C++异常与析构函数及构造函数

析构函数不要抛出异常. 构造函数可以抛出异常,但是要谨慎. 原因下面这篇文章讲的不错,转载如下: http://jarfield.iteye.com/blog/811703 写Java代码的时候,遇到错误总是喜欢抛出异常,简单实用.最近开始写C++代码,发现异常没那么简单,使用须谨慎. 翻阅了<Effective C++> <More Effective C++><Inside The C++ Object Model>的相关章节,大概弄明白了一些东东,总结在本文. 本

C++11 委派构造函数特性怎么使用?

在代码开发中,C语言和C++都是基础语言,是很多web开发人员的入门级必学语言.但在C++98 中,类成员构造问题还存在一些问题,为此C++11提出了一些新特性. C++98中如果一个类有多个构造函数且要实现类成员构造,这些构造函数通常要包含基本相同的类成员构造代码.在最坏的情况下,相同的类成员构造语句被拷贝粘贴在每一个构造函数中. 基于C++98中的类成员构造问题,C++11新特性中,程序员可以将公有的类成员构造代码集中在某一个构造函数里,这个函数被称为目标构造函数.其他构造函数通过调用目标构

C++的构造函数和析构函数

1.构造函数和析构函数为什么没有返回值? 构造函数和析构函数是两个非常特殊的函数:它们没有返回值.这与返回值为void的函数显然不同,后者虽然也不返回任何值,但还可以让它做点别的事情,而构造函数和析构函数则不允许.在程序中创建和消除一个对象的行为非常特殊,就像出生和死亡,而且总是由编译器来调用这些函数以确保它们被执行.如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由客户程序员自己来显式的调用构造函数与析构函数,这样一来,安全性就被人破坏了.另外,析构函数不带任何参数,因为析构不需