问题:
1.如果构造函数中存在new,怎么判断一个对象是否创建成功?
在使用了VC这么长的时间后现在我才知道如果使用new操作符分配内存失败竟会抛出CMemoryException异常。后来我察看了Include下的new.h和new文件,才发现这个运算符已经被MFC封装了。可恶!我一直以为VC的new是标准C++的new,所以,以下的示例代码
int *a = NULL;
a = new int;
if (a == NULL)
{
//Report Error
}
会变得毫无用处,结果我必须处理在数万行代码中所有的new。现在我只想到两个方法,一个是自己重载new操作符,另一个是在所有new的地方都加上try……catch……块。请问高手评论一下哪种方法好
标准C++中定义构造函数是一个对象构建自己,分配所需资源的地方,一旦构造函数执行完毕,则表明这个对象已经诞生了,有自己的行为和内部的运行状态,之后还有对象的消亡过程(析构函数的执行)。可谁能保证对象的构造过程一定能成功呢?说不定系统当前的某个资源不够,导致对象不能完全构建好自己(人都有畸形儿,更何况别的呢?朋友们!是吧!),因此通过什么方法来表明对象的构造失败了呢?C++程序员朋友们知道,C++中的构造函数是没有返回值的,所以不少关于C++编程方面的书上得出结论:“因为构造函数没有返回值,所以通知对象的构造失败的唯一方法那就是在构造函数中抛出异常”。主人公阿愚非常不同意这种说法,谁说的,便不信邪!虽然C++标准规定构造函数是没有返回值,可我们知道每个函数实际上都会有一个返回值的,这个值被保存在eax寄存器中,因此实际上是有办法通过编程来实现构造函数返回一个值给上层的对象创建者。当然即便是构造函数真的不能有返回值,我们也可以通过一个指针类型或引用类型的出参来获知对象的构造过程的状态
class MyTest_Base
{
public:
MyTest_Base (int& status)
{
//do other job
// 由于资源不够,对象构建失败
// 把status置0,通知对象的构建者
status = 0;
}
virtual ~ MyTest_Base ()
{
cout << “销毁一个MyTest_Base类型的对象” << endl;
}
protected:
};
void main()
{
int status;
MyTest_Base obj1(status);
// 检查对象的构建是否成功
if(status ==0) cout << “对象构建失败” << endl;
}
2叫部分构造会怎么办?
class MyTest_Base
{
public:
MyTest_Base (string name = “”) : m_name(name)
{
throw std::exception(“在构造函数中抛出一个异常,测试!”);
cout << “构造一个MyTest_Base类型的对象,对象名为:”<<m_name << endl;
}
virtual ~ MyTest_Base ()
{
cout << “销毁一个MyTest_Base类型的对象,对象名为:”<<m_name << endl;
}
void Func() throw()
{
throw std::exception(“故意抛出一个异常,测试!”);
}
void Other() {}
protected:
string m_name;
};
void main()
{
try
{
// 对象构造时将会抛出异常
MyTest_Base obj1(“obj1”);
obj1.Func();
obj1.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << “unknow exception”<< endl;
}
}
程序的运行结果将会验证:“构造函数中抛出异常将导致对象的析构函数不被执行”
class MyTest_Base
{
public:
MyTest_Base (string name = "") : m_name(name)
{
cout << "构造一个MyTest_Base类型的对象,对象名为:"<<m_name << endl;
}
virtual ~ MyTest_Base ()
{
cout << "销毁一个MyTest_Base类型的对象,对象名为:"<<m_name << endl;
}
void Func() throw()
{
throw std::exception("故意抛出一个异常,测试!");
}
void Other() {}
protected:
string m_name;
};
class MyTest_Parts
{
public:
MyTest_Parts ()
{
cout << "构造一个MyTest_Parts类型的对象" << endl;
}
virtual ~ MyTest_Parts ()
{
cout << "销毁一个MyTest_Parts类型的对象"<< endl;
}
};
class MyTest_Derive : public MyTest_Base
{
public:
MyTest_Derive (string name = "") : m_component(), MyTest_Base(name)
{
throw std::exception("在MyTest_Derive对象的构造函数中抛出了一个异常!");
cout << "构造一个MyTest_Derive类型的对象,对象名为:"<<m_name << endl;
}
virtual ~ MyTest_Derive ()
{
cout << "销毁一个MyTest_Derive类型的对象,对象名为:"<<m_name << endl;
}
protected:
MyTest_Parts m_component;
};
void main()
{
try
{
// 对象构造时将会抛出异常
MyTest_Derive obj1("obj1");
obj1.Func();
obj1.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << "unknow exception"<< endl;
}
}
程序运行的结果是:
构造一个MyTest_Base类型的对象,对象名为:obj1
构造一个MyTest_Parts类型的对象
销毁一个MyTest_Parts类型的对象
销毁一个MyTest_Base类型的对象,对象名为:obj1
在MyTest_Derive对象的构造函数中抛出了一个异常!
上面这个例子中,MyTest_Derive从MyTest_Base继承,同时MyTest_Derive还有一个MyTest_Parts类型的成员变量。现在MyTest_Derive构造的时候,是在父类MyTest_Base已构造完毕和MyTest_Parts类型的成员变量m_component也已构造完毕之后,再抛出了一个异常,这种情况称为对象的部分构造。是的,这种情况很常见,对象总是由不断的继承或不断的聚合而来,对象的构造过程实际上是这些所有的子对象按规定顺序的构造过程,其中这些过程中的任何一个子对象在构造时发生异常,对象都不能说自己完成了全部的构造过程,因此这里就有一个棘手的问题,当发生对象的部分构造时,对象将析构吗?如果时,又将如何析构呢?
从运行结果可以得出如下结论:
(1) 对象的部分构造是很常见的,异常的发生点也完全是随机的,程序员要谨慎处理这种情况;
(2) 当对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构(即异常发生点前面的对象);而还没有开始构建的子对象将不会被构造了(即异常发生点后面的对象),当然它也就没有析构过程了;还有正在构建的子对象和对象自己本身将停止继续构建(即出现异常的对象),并且它的析构是不会被执行的。
构造函数中抛出异常时概括性总结
(1) C++中通知对象构造失败的唯一方法那就是在构造函数中抛出异常;
(2) 构造函数中抛出异常将导致对象的析构函数不被执行;
(3) 当对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构;
(4) 哈哈^-^,其是还是那句话, “C++的异常处理不会破坏任何一条面向对象的特性!”,因此主人公阿愚再次建议朋友们,牢牢记住这一条!
3..如果构造函数中有很多new,有的new成功,有的new失败,失败了怎么处理,会有内存泄露怎么办?
1.在MFC中,new失败会直接抛出异常,不会去执行析构所以会存在内存泄露,并且返回的指针并不会空,所以要判断new失败只能有try-catch,捕捉错误在处理,例子:
#include "StdAfx.h"
#include "test.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
test::test(void)
{
m_IsEce = 0;
int *b = new int [1000];
try
{
a = NULL;
a = new int [500000000];//程序在此处会抛出异常
}
catch(CMemoryException *e)
{
m_IsEce = 1;//如果有异常,则标志位为1;
e->Delete();//如果有异常,删除异常,让函数继续执行,这样程序不会跳转到其他去执行,而跳到析构去释放
//delete[]b//先释放内存再把错误抛出
//throw
}
m_test = 0;
delete []b;//如果抛出异常,如果没有TRY-CATCH则程序不会执行到这里,内存泄露
}
void test::fun(void)
{
if(m_IsEce == 1)//如果异常标志位有效,则功能函数不执行
{
return ;
}
m_test = 5;
}
test::~test(void)
{
delete []a;//如果抛出异常,如果没有TRY-CATHC则程序不会执行析构
}