首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

构造函数产生的错误

2012-03-27 
构造函数产生的异常当构造函数由于产生异常而没有执行完,回调用析构函数不?classSymbolTable{//...private

构造函数产生的异常
当构造函数由于产生异常而没有执行完,回调用析构函数不?

class   SymbolTable
{
        //...
        private:
                  HTable   _htab;
                  StringBuffer   _strbuf;
};

HTable::HTable(int   size):_size(size)
{
        _alist=new   List <int>   [size];
}
如果哈希表的构造函数失败,就不会调用它的析构函数,就会产生内存泄露
如果使用整个SymbolTable对象分配,就不会有此问题

如果在一个构造函数中需要分配多个资源,必须创建子对象来拥有这些资源,作为嵌入对象,即使构造函数在完成前失败,它们也能自动销毁



[解决办法]
_alist ?
用auto_ptr试试看
[解决办法]
1.
HTable::HTable(int size):_size(size) {
_alist = new List <int> [size];
}

new 这里是安全的. 基本上它做两部分工作. 分配足够大小的 raw 内存. 采用 list <int> 的默认构造器和拷贝构造器在这块内存上初始化.

假设, raw 内存分配完成, 而list <int> 构造器失败, 将会回收已经分配的 raw 内存. 不会造成泄露.

2. 但是, 如果这样:
HTable::HTable(int size):_size(size) {
_alist = new List <int> [size];
process(); // #2
}
如果#2的函数调用失败, throw 了 exception, 那内存就leak了. 因此, _alist 采用智能指针管理比较好.
[解决办法]
如果有兴趣,去看 <Exceptional C++> <C与C++中的异常处理> 等资料。
牢记一个指导原则:不要在构造函数里抛异常!
遵循推广原则:不要在任何地方抛异常。
写异常安全的代码是太困难了,而且大部分跑异常的地方都是错误不是异常(也根本就不该使用异常)。
[解决办法]
effective c++有一条说  不要让异常逃离构造函数
[解决办法]
构造函数产生异常,该类的析构函数不会调用.但是在异常前生成的在堆栈上创建的对象会调用他们自己的析构函数析构.但是如果是在堆上分配的,就会资源泄露.
[解决办法]
记住:不要在构造函数里头new.
解决办法:
1,使用成员函数列表:
HTable:HTable(int n):size(n),_alist(new List <int> [size])
{}
2,使用shared_ptr或者aotu_ptr,一般不要使用auto_ptr,而使用shared_ptr
3,写一个init()方法在单独处理new
HTable:HTable(int n):size(n){}
bool Init()
{
_alist=new List <int> [size];

}
[解决办法]
使用成员函数列表-> 使用成员初始化列表
[解决办法]
牢记一个指导原则:不要在构造函数里抛异常!
=====================
up之,呵呵^_^,okokok
[解决办法]
auto_ptr 在这里不适用,因为分配的是数组。建议用 vector <list <int> > 代替
[解决办法]
无需那么复杂,你可以这样:
class A{
type *ptr;
...
init(){ptr = new type;...}
public:
A():ptr(0){init();};
~A(){if(ptr)delete ptr;}
};
相类似处理,无论构造成功与否,都不会造成内存泄露。
[解决办法]
为什么不能在构造函数里面用new呢?
[解决办法]
2 ls

不是不能 new, 他们在说那种方案最优. 是要把 异常 考虑进去.
[解决办法]
> > 牢记一个指导原则:不要在构造函数里抛异常!

这个指导原则仅在整个项目不使用异常时有效。
[解决办法]
换言之,当任何地方都不要抛出异常的时候,构造函数不要抛出异常。

C++ 异常的使用是需要一定技巧的。构造函数需要达到异常的强保证,析构函数需要达到无异常保证。具体参考 http://blog.csdn.net/wingfiring/archive/2006/04/12/660900.aspx


[解决办法]
From taodm:
如果有兴趣,去看 <Exceptional C++> <C与C++中的异常处理> 等资料。
牢记一个指导原则:不要在构造函数里抛异常!
遵循推广原则:不要在任何地方抛异常。
===============================================
很赞成taodm的两条原则. 另外补充一点, <Exceptional C++> 里好象是说在构造函数里可以抛异常,但永远不要在析构函数里抛异常. 析构函数异常引起的问题远比构造函数严重.
[解决办法]
不能 throw exception 的应该是 析构器 和 swap 方法. 构造器有时候是没办法的, 一定抛的可能. 构造器内要使用 new ,就要有抛异常的心理准备.
[解决办法]
从语法上讲,构造函数是可以抛异常的,从程序可控性上讲,就不要在构造函数里抛异常了。
[解决办法]
taodm,看看我提供的引文。构造失败抛出异常极大简化了程序的错误处理逻辑,保证对象处于有效状态,无须用户手工进行二次初始化和检查。
[解决办法]
除了分配资源,还有什么是必须在构造函数体内做的?
[解决办法]
构造函数和异常很有争议,我也不是没争论过。但是不会有什么一致的结果。

[解决办法]
taodm. 这个异常未必是用户异常. 有可能是 new 产生的.
[解决办法]
yutaooo说得对,throw Excpeption与产生Exception是两码事.构造函数中我们可以避免前一种情况,但无法保证不出现后一种情况(例如new产生异常).
new单个对象时有一种很好的方法保证异常是安全的: 使用auto_ptr(). 当异常发生时,auto_ptr会调用所指对象的析构造函数释放资源. new一个数组时就不知道有什么好方法了.
[解决办法]
如果构造里抛出异常

直接调用delete了

注意,是相对应的delete,如果没有,就泄露了

[解决办法]
我觉得构造器还是要让它抛异常的. 一个原因是 new 及其它分配器可能抛异常, 防不住. 另一个是第三方用户类的构造器可能抛异常, 而是个成员变量.

这个防起来就麻烦了. 也许要这种try的用法, 并且后续还有问题.
test::test()
try
: dm1(),
dm2(new some_class)
{
}
catch(...) {
// 处理
}

如果分配失败了, 而用户没有任何通知就使用对象, 这有问题. 析构清理的时候也有很多不便.
[解决办法]
构造函数是可以抛出异常的,前提是它确实是一个异常。
异常是指不可避免地会发生地事,而错误是指编程者自己可以避免地事。如一个除0异常。对编程者而言它是一个错误,是可以避免地(假设 1/0 是编程者自己写的话),而对编译器来说,它不能假设使用它的编程者不犯这个错,因此除0对它而言是个异常,最好的办法就是抛出这个异常,让犯错的人自己去处理它。
所以,如果在构造函数中必须抛出异常,那一定是类设计者(写构造函数的人)自己无法解决的问题(即使自己可以解决,也不要做多余的事,因为你的解决方法可能不是使用者想要的),只有类使用者(调用构造函数的人)可能解决它,因为造成这个问题的错误也可能出自他。如果类设计者自己犯了一个错,那么就不应该抛出异常,让使用者去承受后果。

此外,构造函数有很好的异常应对语义,当它抛出异常时,它一定会释放分配给自己的内存,这就好比调用了一个什么也不做的析构函数一样(有的编译器就是这么做的,每一个类都有一个什么也不做的析构函数,只用来在构造失败时释放内存,用户不能访问它,也不能修改它;而当用户不显式定义析构函数时,系统也使用它来析构对象)。但注意了,如果不是对象本身的内存,是不会被释放的!比如一个指针成员,指针本身的内存是对象内存的一部分,但指针指向的内存则不是。因为,你怎么能要求编译器判断这个指针指向的内存是怎么来的呢?从堆中来、从栈中来、从其他指针赋值过来...这些都是由类定义者决定的,因为也由类定义者负责处理。对于构造中用到的new,以及构造中抛出的异常,不要一味地去避免它。而是当两者同时发生时,觉悟到避免它们出错正是你的责任,那么你一定可以处理好它们。

热点排行