对于像所有权语义这样的原始指针返回unique_ptr的不良做法?

Bre*_*hns 49 c++ smart-pointers ownership-semantics unique-ptr

我编写了一个静态工厂方法,它返回从另一个数据对象填充的新Foobar对象.我最近一直沉迷于所有权语义,我想知道我是否通过这种工厂方法返回一个正确的信息unique_ptr.

class Foobar {
public:
    static unique_ptr<Foobar> factory(DataObject data);
}
Run Code Online (Sandbox Code Playgroud)

我的目的是告诉客户端代码他们拥有指针.没有智能指针,我只会回来Foobar*.但是,我想强制删除这个内存以避免潜在的错误,所以这unique_ptr似乎是一个合适的解决方案.如果客户端想要延长指针的生命周期,他们只需在调用.release()后调用unique_ptr.

Foobar* myFoo = Foobar::factory(data).release();
Run Code Online (Sandbox Code Playgroud)

我的问题分为两部分:

  1. 这种方法是否传达了正确的所有权语义?
  2. 返回unique_ptr而不是原始指针这是一个"坏习惯" 吗?

Gri*_*zly 60

std::unique_ptr从工厂方法返回是很好的,应该是一个推荐的做法.它传达的信息是(IMO):您现在是该对象的唯一所有者.此外,为方便起见,对象知道如何破坏自己.

我认为这比返回一个原始指针要好得多(客户端必须记住如何处理这个指针).

但是我不明白你关于释放指针以扩展它的生命周期的评论.总的来说,我很少看到任何理由骂release上一个智能指针,因为我觉得指针应该总是通过某种RAII结构(只是这里我所说的只是情况的管理release是把指针在不同的管理数据结构,例如unique_ptr用一个不同的删除者,在我做了一些事情以保证额外的清理之后).

因此,只要客户端需要对象(或者,如果他们需要指针的多个副本),客户端就可以(并且应该)简单地存储unique_ptr某个地方(例如另一个unique_ptr已经从返回的移动构造的移动shared_ptr).因此客户端代码看起来应该更像这样:

std::unique_ptr<FooBar> myFoo = Foobar::factory(data);
//or:
std::shared_ptr<FooBar> myFoo = Foobar::factory(data);
Run Code Online (Sandbox Code Playgroud)

我个人也会为你的工厂对象添加一个typedef返回的指针类型(在这种情况下std::unique_ptr<Foobar>)和/或使用的删除器(在本例中为std :: default_deleter).如果您以后决定更改指针的分配(因此需要一种不同的方法来销毁指针,这将作为第二个模板参数可见),这会更容易std::unique_ptr.所以我会做这样的事情:

class Foobar {
public:  
    typedef std::default_deleter<Foobar>     deleter;
    typedef std::unique_ptr<Foobar, deleter> unique_ptr;

    static unique_ptr factory(DataObject data);
}

Foobar::unique_ptr myFoo = Foobar::factory(data);
//or:
std::shared_ptr<Foobar> myFoo = Foobar::factory(data);
Run Code Online (Sandbox Code Playgroud)

  • 我并不是说我提到'release()`会产生误导或混淆,抱歉.这个新代码将进入一个相当大的现有应用程序(1-2百万行C++),它不使用任何智能指针(COM除外).我知道我的团队中的其他人会担心如果遗留代码使其有效"需要",则没有合理的方法来获取原始指针.当然,我会给出正确的警告,并建议尽可能坚持使用unique_ptr/shared_ptr.我非常喜欢你的typedef想法,这让你的答案与James McNellis不同.感谢您的见解. (3认同)

Jam*_*lis 17

一个std::unique_ptr唯一拥有它指向的对象.它说"我拥有这个对象,而没有其他人这样做."

这正是你想要表达的:你说的是"这个功能的来电者:你现在是这个对象的唯一所有者;随你做它,它的一生就是你的责任."

  • @AndréCaron:如果您想要做的就是将所有权转移给调用者,为什么强制使用`std :: shared_ptr`?如果调用者想要(或者其他类型的智能指针,如果调用者想要的话),让调用者将对象的所有权赋予`std :: shared_ptr`. (8认同)
  • @AndréCaron:原因基本上是表现.工厂无法知道客户端是否会通过`std :: shared_ptr`添加`std :: shared_ptr`的功能,所以为什么牺牲某些功能的性能,这可能不是必需的.由于`shared_ptr`可以构造并从`unique_ptr`分配,所以实际上没有任何好处 (7认同)

Die*_*ühl 6

它确切地传达了正确的语义,并且是我认为C++中的所有工厂都应该工作的方式:std::unique_ptr<T>不强加任何所有权语义,而且它非常便宜.