ar1*_*r1a 2 c++ pointers destructor unique-ptr
说我有这个班:
class Foo
{
public:
Foo()
{
bar = new Bar;
}
~Foo()
{
if(bar)
delete bar;
}
private:
Bar* bar;
};
Run Code Online (Sandbox Code Playgroud)
为什么我要std::unique_ptr在unique_ptr膨胀时使用而不是原始指针?有什么优势吗?有没有我的析构函数不会被调用的情况?
tem*_*def 31
您上面的代码实际上有一个错误,因为您没有定义复制构造函数或赋值运算符.想象一下这段代码:
Foo one;
Foo two = one;
Run Code Online (Sandbox Code Playgroud)
因为two是one它的副本,所以使用默认的复制构造函数初始化它 - 这使得两个bar指针指向同一个对象.这意味着当用于two触发的析构函数时,它将释放共享的同一个对象one,因此one析构函数将触发未定义的行为.哎呀.
现在,如果你不想让你的对象可以复制,你可以明确地这样说:
class Foo
{
public:
Foo()
{
bar = new Bar;
}
Foo(const Foo&) = delete;
Foo& operator= (const Foo&) = delete;
~Foo()
{
if(bar)
delete bar;
}
private:
Bar* bar;
};
Run Code Online (Sandbox Code Playgroud)
所以这解决了这个问题 - 但看看涉及的代码量!您必须明确删除两个函数并手动编写析构函数.
除了另一个问题.假设我这样做:
Foo one;
Foo two = std::move(one);
Run Code Online (Sandbox Code Playgroud)
这个初始化two通过移动内容one到two.或者是吗?不幸的是,答案是否定的,因为默认的移动构造函数将默认移动指针,这只是一个直指针复制.所以现在你得到了和以前一样的东西.哎呀.
不用担心!我们可以通过定义自定义移动构造函数和移动赋值运算符来解决此问题:
class Foo
{
public:
Foo()
{
bar = new Bar;
}
Foo(const Foo&) = delete;
Foo& operator= (const Foo&) = delete;
Foo(Foo&& rhs)
{
bar = rhs.bar;
rhs.bar = nullptr;
}
Foo& operator= (Foo&& rhs)
{
if (bar != rhs.bar)
{
delete bar;
bar = rhs.bar;
rhs.bar = nullptr;
}
}
~Foo()
{
if(bar)
delete bar;
}
private:
Bar* bar;
};
Run Code Online (Sandbox Code Playgroud)
唷!这是一大堆代码,但至少它是正确的.(或者是吗?)
另一方面,想象你写了这个:
class Foo
{
public:
Foo() : bar(new Bar) {
}
private:
std::unique_ptr<Bar> bar;
};
Run Code Online (Sandbox Code Playgroud)
哇,这短得多!它会自动确保类不能被复制,并且它使得默认移动构造函数和移动赋值操作符正常工作.
因此,一个巨大的优势std::unique_ptr是它会自动处理资源管理,是的,但另一个优点是它可以很好地处理复制和移动语义,并且不会以意想不到的方式工作.这是使用它的主要原因之一.你可以说出你的意思 - "我是唯一应该了解这件事的人,你不能分享它" - 编译器会为你强制执行它.争取编译器帮助您避免错误几乎总是一个好主意.
至于臃肿 - 我需要看到证据.std::unique_ptr是一个指针类型的薄包装器,一个好的优化编译器应该没有问题为它生成良好的代码.确实,有与之相关的构造函数,析构函数等std::unique_ptr,但是合理的编译器会内联这些调用,这实际上与您最初描述的内容完全相同.