为像所有权语义这样的原始指针返回 unique_ptr 是不好的做法?

发布于 2024-12-24 19:48:08 字数 592 浏览 2 评论 0原文

我编写了一个静态工厂方法,它返回一个由另一个数据对象填充的新 Foobar 对象。我最近痴迷于所有权语义,并且想知道我是否通过让此工厂方法返回 unique_ptr 来传达正确的消息。

class Foobar {
public:
    static unique_ptr<Foobar> factory(DataObject data);
}

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

Foobar* myFoo = Foobar::factory(data).release();

我的问题分为两部分:

  1. 这种方法是否传达了正确的所有权语义?
  2. 返回unique_ptr而不是原始指针是“不好的做法”吗?

I've written a static factory method that returns a new Foobar object populated from another data object. I've recently been obsessed with ownership semantics and am wondering if I'm conveying the right message by having this factory method return a unique_ptr.

class Foobar {
public:
    static unique_ptr<Foobar> factory(DataObject data);
}

My intent is to tell client code that they own the pointer. Without a smart pointer, I would simply return Foobar*. I would like, however, to enforce that this memory be deleted to avoid potential bugs, so unique_ptr seemed like an appropriate solution. If the client wants to extend the lifetime of the pointer, they just call .release() once they get the unique_ptr.

Foobar* myFoo = Foobar::factory(data).release();

My question comes in two parts:

  1. Does this approach convey the correct ownership semantics?
  2. Is this a "bad practice" to return unique_ptr instead of a raw pointer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

樱桃奶球 2024-12-31 19:48:08

从工厂方法返回 std::unique_ptr 就可以了,应该是推荐的做法。它传达的信息是(IMO):您现在是该对象的唯一所有者。此外,为了您的方便,该对象知道如何销毁自身。

我认为这比返回原始指针要好得多(客户端必须记住如何以及是否处置该指针)。

但是我不明白您关于释放指针以延长其生命周期的评论。一般来说,我很少看到任何理由在智能指针上调用 release ,因为我认为指针应该始终由某种 RAII 结构管理(这几乎是我调用 release 的唯一情况) code> 是将指针放入不同的管理数据结构中,例如,在我做了一些需要额外清理的操作之后,将指针放在不同的管理数据结构中,例如具有不同删除器的 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);

就我个人而言,我还会为返回的指针类型添加一个 typedef (在本例中为 std::unique_ptr),或者使用的删除器(在本例中为 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);

Returning a std::unique_ptr from a factory method is just fine and should be a recommended practice. The message it conveys is (IMO): You are now the sole owner of this object. Furthermore, for your convenience, the object knows how to destroy itself.

I think this is much better then returning a raw pointer (where the client has to remember how and if to dispose of this pointer).

However I do not understand your comment about releasing the pointer to extend it's lifetime. In general I rarely see any reason to call release on a smartpointer, since I think pointers should always be managed by some sort of RAII structure (just about the only situation where I call release is to put the pointer in a different managing datastructure, e.g. a unique_ptr with a different deleter, after I did something to warrant additional cleanup) .

Therefore the client can (and should) simply store the unique_ptr somewhere (such as another unique_ptr, which has been move constructed from the returned one) as long as they need the object (or a shared_ptr, if they need multiple copies of the pointer). So the clientside code should look more like this:

std::unique_ptr<FooBar> myFoo = Foobar::factory(data);
//or:
std::shared_ptr<FooBar> myFoo = Foobar::factory(data);

Personally I would also add a typedef for the returned pointer type (in this case std::unique_ptr<Foobar>) and or the used deleter (in this case std::default_deleter) to your factory object. That makes it easier if you later decide to change the allocation of your pointer(and therefore need a different method for destruction of the pointer, which will be visible as a second template parameter of std::unique_ptr).
So I would do something like this:

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);
轻拂→两袖风尘 2024-12-31 19:48:08

std::unique_ptr 唯一地拥有它所指向的对象。它说“我拥有这个物体,其他人都没有。”

这正是您想要表达的:您是在说“此函数的调用者:您现在是此对象的唯一所有者;您可以随意使用它,它的生命周期是您的责任。”

A std::unique_ptr uniquely owns the object to which it points. It says "I own this object, and no one else does."

That is exactly what you are trying to express: you are saying "caller of this function: you are now the sole owner of this object; do with it as you please, its lifetime is your responsibility."

烛影斜 2024-12-31 19:48:08

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

It exactly conveys the correct semantics and is the way I think all factories in C++ should work: std::unique_ptr<T> doesn't impose any kind of ownership semantics and it is extremely cheap.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文