【问题标题】:User-friendly API with unique_ptr具有 unique_ptr 的用户友好 API
【发布时间】:2017-03-22 20:04:45
【问题描述】:

我正在为 OpenGL 实现一个简单的 GUI,主要是作为我自己的练习。这个想法是有一个 Gui 类,其中每个实例都可以分配给不同的渲染目标(例如后台缓冲区或纹理)。 GUI 元素(小部件)被分配给 Gui 类的一个实例。我想在 GUI 中存储元素是unique_ptr 的典型用例。这是我想出的:

class Element {
public:
    Element();
    virtual ~Element();

    static std::unique_ptr<Element> create_unique();
};

class Gui {
public:    
    typedef std::unique_ptr<Element> element_ptr;    
    Gui();
    void addElement( element_ptr element );
    void addElementRaw( Element* element );

private:
    std::list<element_ptr> elements;
};

int main(void) {
    Gui gui;

    gui.addElementRaw( new Element() ); // 1

    gui.addElement( Element::create_unique() );  // 2

    gui.addElement( std::unique_ptr<Element>(new Element()) ); // 3

    auto el = Element::create_unique();
    gui.addElement( std::move(el) ); // 4
}

我不希望 GUI 的潜在用户担心移动指针。但是,我想通过 API 明确说明 GUI 类拥有元素的所有权。

  1. 传递原始指针:简单用法,但 API 并未明确说明所有权已传递。
  2. 工厂函数:使用简单,但是对于从Element派生的每个类都需要重新实现该函数。
  3. 手动创建 unique_ptr:对用户来说很麻烦
  4. 移动语义:看起来也很麻烦。

我对我的解决方案不满意。我想要的是 (1) 的简单性,同时通过 API 清楚地表明 gui 现在拥有该元素。

【问题讨论】:

  • #3 在 C++1y 中将被简化为 std::make_unique
  • 是的,但是用户仍然需要为涉及到 unique_ptr 的事实而烦恼......但是,毕竟这可能是可取的。
  • 我建议快速阅读this GOTW

标签: c++ c++11 smart-pointers


【解决方案1】:

为新元素提供参数怎么样?

template< typename... T >
void addElement( T&&... t )
{
    elements.emplace_back( std::unique_ptr< Element >( new Element( std::forward< T >( t )... ) ) );
}

对于 C++14,您还可以使用 std::make_unique:

template< typename... T >
void addElement( T&&... t )
{
    elements.emplace_back( std::make_unique< Element >( std::forward< T >( t )... ) );
}

如果你想创建从Element 派生的元素,你也可以这样做(C++14 版本):

template< typename C, typename... T >
void emplace_back( T&&... t )
{
    elements.emplace_back( std::make_unique< C >( std::forward< T >( t )... ) );
}

而且可以这样使用:

gui.emplace_back< Element >();

// insert a class derived from Element (hope you have a virtual dtor!)
gui.emplace_back< DerivedFromElement >();

// calls Element::Element( int, const char* ) or similar...
gui.emplace_back< Element >( 42, "Hallo" );

【讨论】:

  • +1 但我会称它为emplaceElement 并保留现有的addElement(并将addElementRaw 重命名为addElement
  • @Praetorian 在写答案时,我最初称它为emplaceElement,所以是的,这似乎是个好主意。但是 OTOH 它可能会令人困惑,因为它插入了 unique_ptr 而不是原始的 element,所以我将把它留给 OP 来决定什么最适合他的用例。毕竟,API 不应该反映实现细节,而是传达语义。
  • 这可能会泄漏吗?我的意思是,emplace_back 需要先分配,然后从参数就地构造新元素。如果分配失败,则参数只是一个原始指针,可能会泄露。对吗?
  • @DyP 好收获!是的,它可能会泄漏,我修复了答案。
  • 鉴于这是一个 GUI 框架,客户很可能希望对新元素进行一些额外的设置。我会从addElement 返回一个参考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多