下面的代码会产生内存泄漏吗?
#include <utility>
#include <stdexcept>
struct A {};
struct B {
B() {
throw std::runtime_error("");
}
};
template <class T>
class MyPtr {
public:
MyPtr(T* p) : m_p(p) {}
MyPtr(MyPtr&& other) : m_p(other.m_p) {
other.m_p = nullptr;
}
~MyPtr() {
delete m_p;
}
private:
T* m_p;
};
class Foo {
public:
Foo(MyPtr<A> a, MyPtr<B> b) : m_a(std::move(a)), m_b(std::move(b)) {}
private:
MyPtr<A> m_a;
MyPtr<B> m_b;
};
int main() {
try {
Foo foo(new A(), new B());
}
catch (const std::exception&) {
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
之间有区别吗
Foo foo(new A(), new B());
Run Code Online (Sandbox Code Playgroud)
和
Foo foo(MyPtr(new A()), MyPtr(new B()));
Run Code Online (Sandbox Code Playgroud)
?
在现代 C++ 版本中,求值顺序基于顺序前和顺序后。这意味着,将会发生以下情况:
new A(), new B()- 为 和 分配内存A,并B运行构造函数(这些不能交错)B有一个 ctor,因此该 ctor 会运行(分配后排序)B的throw演员B的 ctor 没有完成,因此new 构造时B分配的内存将被释放,而不调用 dtor。请注意,对 的 ctor 的调用Foo将在上述之后进行排序,因此不会到达它。
因此,B不被泄露。但是,函数参数可以以任何不重叠的方式排序,因此A可能会也可能不会泄漏。从理论上讲,这可能会因运行而异。
如果你这样做:Foo foo(MyPtr(new A()), MyPtr(new B()));,那么你会立即将指针封装到之后的智能指针new,这样就不会泄漏。
另外,值得一提的是,虽然 for 的内存B 被B释放,但在的 ctor 中分配且未释放的任何内存throw仍然可能会泄漏。在本示例中没有这样的分配,但在实际代码中可能会发生。唯一释放的内存是正在构造的整个对象的内存。