什么是工厂函数的最好的智能指针返回类型? [英] What is the best smart pointer return type for a factory function?

查看:139
本文介绍了什么是工厂函数的最好的智能指针返回类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于智能指针和新的C ++ 11/14功能,我想知道最好的实践返回值和函数参数类型是有这些设施的类:

With respect to smart pointers and new C++11/14 features, I am wondering what the best-practice return values and function parameter types would be for classes that have these facilities:


  1. 一个工厂函数(类外),它创建对象并将其返回给类的用户。 (例如打开文档并返回可用于访问内容的对象。)

  1. A factory function (outside of the class) that creates objects and returns them to users of the class. (For example opening a document and returning an object that can be used to access the content.)

从工厂函数接受对象的实用程序函数, ,但不采取所有权。 (例如计算文档中单词数量的函数。)

Utility functions that accept objects from the factory functions, use them, but do not take ownership. (For example a function that counts the number of words in the document.)

返回后保留对对象引用的函数它需要一个对象的副本,所以它可以根据需要在屏幕上绘制内容。)

Functions that keep a reference to the object after they return (like a UI component that takes a copy of the object so it can draw the content on the screen as needed.)


  • 如果它是一个原始指针,用户必须具有<$

  • 如果返回 unique_ptr<>

  • 如果是 shared_ptr<> ,那么我必须通过周围 shared_ptr<> 类型无处不在?这是我现在正在做的,它正在导致问题,因为我得到循环引用,防止对象被自动销毁。

  • If it's a raw pointer the user will have to delete it correctly which is problematic.
  • If it returns a unique_ptr<> then the user can't share it if they want to.
  • If it's a shared_ptr<> then will I have to pass around shared_ptr<> types everywhere? This is what I'm doing now and it's causing problems as I'm getting cyclic references, preventing objects from being destroyed automatically.

效用函数的最佳参数类型是什么?


  • 我想通过引用传递将避免增加智能指针引用计数不必要,但有什么缺点吗?我想到的主要是,它阻止我将派生类传递给接受基类类型参数的函数。

  • 有一些方法,我可以清楚地告诉调用者,它将不会复制对象? (理想情况下,如果函数体尝试复制对象,代码将无法编译。)

  • 是否有独立于使用中的智能指针类型的方法? (可能使用原始指针?)

  • 可以有一个 const 参数,以清除函数不会修改对象,而不会破坏智能指针的兼容性?

  • I imagine passing by reference will avoid incrementing a smart pointer reference count unnecessarily, but are there any drawbacks of this? The main one that comes to mind is that it prevents me from passing derived classes to functions taking parameters of the base-class type.
  • Is there some way that I can make it clear to the caller that it will NOT copy the object? (Ideally so that the code will not compile if the function body does try to copy the object.)
  • Is there a way to make it independent of the type of smart pointer in use? (Maybe taking a raw pointer?)
  • Is it possible to have a const parameter to make it clear the function will not modify the object, without breaking smart pointer compatibility?

保存对象引用的函数的最佳参数类型是什么


  • 我猜猜 shared_ptr<> 只有选项在这里,这可能意味着工厂类必须返回一个 shared_ptr<> 也正确吗?

  • I'm guessing shared_ptr<> is the only option here, which probably means the factory class must return a shared_ptr<> also, right?

这里是一些编译并希望说明要点的代码。

Here is some code that compiles and hopefully illustrates the main points.

#include <iostream>
#include <memory>

struct Document {
    std::string content;
};

struct UI {
    std::shared_ptr<Document> doc;

    // This function is not copying the object, but holding a
    // reference to it to make sure it doesn't get destroyed.
    void setDocument(std::shared_ptr<Document> newDoc) {
        this->doc = newDoc;
    }
    void redraw() {
        // do something with this->doc
    }
};

// This function does not need to take a copy of the Document, so it
// should access it as efficiently as possible.  At the moment it
// creates a whole new shared_ptr object which I feel is inefficient,
// but passing by reference does not work.
// It should also take a const parameter as it isn't modifying the
// object.
int charCount(std::shared_ptr<Document> doc)
{
    // I realise this should be a member function inside Document, but
    // this is for illustrative purposes.
    return doc->content.length();
}

// This function is the same as charCount() but it does modify the
// object.
void appendText(std::shared_ptr<Document> doc)
{
    doc->content.append("hello");
    return;
}

// Create a derived type that the code above does not know about.
struct TextDocument: public Document {};

std::shared_ptr<TextDocument> createTextDocument()
{
    return std::shared_ptr<TextDocument>(new TextDocument());
}

int main(void)
{
    UI display;

    // Use the factory function to create an instance.  As a user of
    // this class I don't want to have to worry about deleting the
    // instance, but I don't really care what type it is, as long as
    // it doesn't stop me from using it the way I need to.
    auto doc = createTextDocument();

    // Share the instance with the UI, which takes a copy of it for
    // later use.
    display.setDocument(doc);

    // Use a free function which modifies the object.
    appendText(doc);

    // Use a free function which doesn't modify the object.
    std::cout << "Your document has " << charCount(doc)
        << " characters.\n";

    return 0;
}


推荐答案


实用函数接受来自工厂函数的对象,使用它们,但不占有所有权。 (例如,计算文档中单词数量的函数。)

Utility functions that accept objects from the factory functions, use them, but do not take ownership. (For example a function that counts the number of words in the document.)

如果您正在调用一个工厂函数,通过工厂函数的非常定义获取所创建对象的所有权。我想你的意思是,一些其他客户端首先从工厂获取一个对象,然后希望将其传递给不采取所有权本身的效用函数。

If you are calling a factory function, you are always taking ownership of the created object by the very definition of a factory function. I think what you mean is that some other client first obtains an object from the factory and then wishes to pass it to the utility function that does not take ownership itself.

在这种情况下,效用函数不应该关心它操作的对象的所有权是如何管理的。它应该简单地接受一个(可能 const )引用或 - 如果无对象是一个有效的条件 - 一个非拥有的原始指针。这将最小化您的接口之间的耦合,并使实用程序函数最灵活。

In this case, the utility function should not care at all how ownership of the object it operates on is managed. It should simply accept a (probably const) reference or – if "no object" is a valid condition – a non-owning raw pointer. This will minimize the coupling between your interfaces and make the utility function most flexible.


返回后保留对对象引用的函数就像一个UI组件,它需要一个对象的副本,所以它可以根据需要在屏幕上绘制内容。)

Functions that keep a reference to the object after they return (like a UI component that takes a copy of the object so it can draw the content on the screen as needed.)

std :: shared_ptr 由值。从函数的签名中可以清楚地看出,它们对参数拥有共享的所有权。

These should take a std::shared_ptr by value. This makes it clear from the function's signature that they take shared ownership of the argument.

有时,拥有一个拥有其参数的唯一所有权的函数也是有意义的(构造函数)。这些应采用 std :: unique_ptr <

Sometimes, it can also be meaningful to have a function that takes unique ownership of its argument (constructors come to mind). Those should take a std::unique_ptr by value (or by rvalue reference) which will also make the semantics clear from the signature.


一个工厂函数(在类外部)创建对象并将它们返回给类的用户。 (例如打开文档并返回可用于访问内容的对象。)

A factory function (outside of the class) that creates objects and returns them to users of the class. (For example opening a document and returning an object that can be used to access the content.)

这是一个困难的是 std :: unique_ptr std :: shared_ptr 的好参数。唯一清楚的是返回一个拥有的原始指针是不好的。

This is the difficult one as there are good arguments for both, std::unique_ptr and std::shared_ptr. The only thing clear is that returning an owning raw pointer is no good.

返回 std :: unique_ptr 轻量级(与返回原始指针相比没有开销),并传达工厂函数的正确语义。调用该函数的任何人都获得对制造对象的独占所有权。如果需要,客户端可以以 std :: unique_ptr 构建 std :: shared_ptr 动态内存分配。

Returning a std::unique_ptr is lightweight (no overhead compared to returning a raw pointer) and conveys the correct semantics of a factory function. Whoever called the function obtains exclusive ownership over the fabricated object. If needed, the client can construct a std::shared_ptr out of a std::unique_ptr at the cost of a dynamic memory allocation.

另一方面,如果客户端需要一个 std :: shared_ptr ,让工厂使用 std :: make_shared 以避免额外的动态内存分配。此外,有些情况下你只需要使用 std :: shared_ptr 例如,如果被管对象的析构函数是非< - code> virtual ,并且智能指针将被转换为基类的智能指针。但是 std :: shared_ptr std :: unique_ptr 有更多的开销,因此如果后者足够了,

On the other hand, if the client is going to need a std::shared_ptr anyway, it would be more efficient to have the factory use std::make_shared to avoid the additional dynamic memory allocation. Also, there are situations where you simply must use a std::shared_ptr for example, if the destructor of the managed object is non-virtual and the smart pointer is to be converted to a smart pointer to a base class. But a std::shared_ptr has more overhead than a std::unique_ptr so if the latter is sufficient, we would rather avoid that if possible.

最后,我想出了以下指南:

So in conclusion, I'd come up with the following guideline:


  • 如果您需要自定义删除程序,请返回 std :: shared_ptr

  • 你的大多数客户都需要一个 std :: shared_ptr 无论如何,利用优化潜力 std :: make_shared

  • 否则返回 std :: unique_ptr

  • If you need a custom deleter, return a std::shared_ptr.
  • Else, if you think that most of your clients are going to need a std::shared_ptr anyway, utilize the optimization potential of std::make_shared.
  • Else, return a std::unique_ptr.

当然,您可以通过提供两个工厂函数来避免这个问题,其中一个返回 std :: unique_ptr 和一个返回 std :: shared_ptr ,以便每个客户端可以使用最适合其需求。如果你经常需要这个,我想你可以抽象大部分的冗余与一些聪明的模板元程序设计。

Of course, you could avoid the problem by providing two factory functions, one that returns a std::unique_ptr and one that returns a std::shared_ptr so each client can use what best fits its needs. If you need this frequently, I guess you can abstract most of the redundancy away with some clever template meta-programming.

这篇关于什么是工厂函数的最好的智能指针返回类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆