class someclass {};
class base
{
int a;
int *pint;
someclass objsomeclass;
someclass* psomeclass;
public:
base()
{
objsomeclass = someclass();
psomeclass = new someclass();
pint = new int();
throw "constructor failed";
a = 43;
}
}
int main()
{
base temp();
}
在上面的代码中,构造函数抛出。哪些对象会被泄露,以及如何避免内存泄漏?
int main()
{
base *temp = new base();
}
上面的代码怎么样?构造函数抛出后如何避免内存泄漏?
答案 0 :(得分:36)
是的,它会泄漏内存。当构造函数抛出时,不会调用析构函数(在这种情况下,您不会显示释放动态分配的对象的析构函数,但假设您有一个)。
这是使用智能指针的一个主要原因 - 因为智能助手是完全成熟的对象,他们将在异常堆栈中调用析构函数,并有机会释放内存。
如果你使用类似Boost的scoped_ptr<>模板,你的类看起来更像:
class base{
int a;
scoped_ptr<int> pint;
someclass objsomeclass;
scoped_ptr<someclass> psomeclass;
base() :
pint( new int),
objsomeclass( someclass()),
psomeclass( new someclass())
{
throw "constructor failed";
a = 43;
}
}
并且你没有内存泄漏(默认的dtor也会清理动态内存分配)。
总结(希望这也回答了关于
的问题base* temp = new base();
语句):
当在构造函数中抛出异常时,在正确处理可能在对象的中止构造中发生的资源分配方面,您应该注意以下几点:
这意味着如果您的对象拥有资源,您可以使用2种方法来清理构造函数抛出时可能已经获取的资源:
答案 1 :(得分:5)
两个新人都将被泄露。
将堆创建的对象的地址分配给命名的智能指针,以便在抛出异常时获取调用的智能指针析构函数中删除它 - (RAII)。
class base {
int a;
boost::shared_ptr<int> pint;
someclass objsomeclass;
boost::shared_ptr<someclass> psomeclass;
base() :
objsomeclass( someclass() ),
boost::shared_ptr<someclass> psomeclass( new someclass() ),
boost::shared_ptr<int> pint( new int() )
{
throw "constructor failed";
a = 43;
}
};
现在 psomeclass &amp;当构造函数中抛出异常时,当堆栈展开时,将调用 pint 析构函数,并且这些析构函数将释放已分配的内存。
int main(){
base *temp = new base();
}
对于使用(non-plcaement)new的普通内存分配,如果构造函数抛出异常,则由operator new分配的内存将自动释放。就为什么要解放个别成员而言(响应对Mike B的回答的评论),自动释放仅适用于在新分配的对象的构造函数中抛出异常,而不是在其他情况下。此外,释放的内存是为对象成员分配的内存,而不是您在构造函数中分配的任何内存。即它将释放成员变量 a , pint , objsomeclass 和 psomeclass 的内存,但不是从 new someclass()和 new int()分配的内存。
答案 2 :(得分:1)
我认为最重要的答案是错误的,但仍然会泄漏内存。 如果构造函数抛出异常(因为它从未完成其初始化,并且可能某些成员从未到过构造函数调用),则类成员的析构函数将 not 被调用。 他们的析构函数只在类的析构函数调用期间调用。这才有意义。
这个简单的程序演示了它。
#include <stdio.h>
class A
{
int x;
public:
A(int x) : x(x) { printf("A constructor [%d]\n", x); }
~A() { printf("A destructor [%d]\n", x); }
};
class B
{
A a1;
A a2;
public:
B()
: a1(3),
a2(5)
{
printf("B constructor\n");
throw "failed";
}
~B() { printf("B destructor\n"); }
};
int main()
{
B b;
return 0;
}
使用以下输出(使用g ++ 4.5.2):
A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted
如果您的构造函数失败了,那么您有责任处理它。更糟糕的是,可能会从基类的构造函数中抛出异常! 处理这些情况的方法是使用“函数try块”(但即使这样,你也必须仔细编译部分初始化对象的破坏)。
正确解决问题的方法是:
#include <stdio.h>
class A
{
int x;
public:
A(int x) : x(x) { printf("A constructor [%d]\n", x); }
~A() { printf("A destructor [%d]\n", x); }
};
class B
{
A * a1;
A * a2;
public:
B()
try // <--- Notice this change
: a1(NULL),
a2(NULL)
{
printf("B constructor\n");
a1 = new A(3);
throw "fail";
a2 = new A(5);
}
catch ( ... ) { // <--- Notice this change
printf("B Cleanup\n");
delete a2; // It's ok if it's NULL.
delete a1; // It's ok if it's NULL.
}
~B() { printf("B destructor\n"); }
};
int main()
{
B b;
return 0;
}
如果你运行它,你将获得预期的输出,其中只有被分配的对象被销毁和释放。
B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted
如果您愿意,还可以使用智能共享指针进行额外复制。编写类似于此的构造函数:
class C
{
std::shared_ptr<someclass> a1;
std::shared_ptr<someclass> a2;
public:
C()
{
std::shared_ptr<someclass> new_a1(new someclass());
std::shared_ptr<someclass> new_a2(new someclass());
// You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
a1 = new_a1;
a2 = new_a2;
}
}
祝你好运,
Tzvi。
答案 3 :(得分:0)
是的,该代码会泄漏内存。引发异常时,不会释放使用“new”分配的内存块。这是RAII背后动机的一部分。
为避免内存泄漏,请尝试以下方法:
psomeclass = NULL;
pint = NULL;
/* So on for any pointers you allocate */
try {
objsomeclass = someclass();
psomeclass = new someclass();
pint = new int();
throw "constructor failed";
a = 43;
}
catch (...)
{
delete psomeclass;
delete pint;
throw;
}
答案 4 :(得分:0)
如果你引入一个构造函数,你应该清理在throw之前的所有内容。如果你使用继承或抛出析构函数,你真的不应该。行为是奇怪的(没有我的标准方便,但它可能是未定义的?)。
答案 5 :(得分:-2)
您需要删除所有“新”内容,否则会导致内存泄漏。所以这两行:
psomeclass = new someclass();
pint = new int();
会导致内存泄漏,因为您需要这样做:
delete pint;
delete psomeclass;
在最后一个块中,以避免它们被泄露。
此外,这一行:
base temp = base();
没必要。你只需要这样做:
base temp;
不需要添加“= base()”。
答案 6 :(得分:-3)
你需要删除psomeclass ...没有必要清理整数......