【问题标题】:How to use references, avoid header bloat, and delay initialization?如何使用引用、避免标题膨胀和延迟初始化?
【发布时间】:2010-03-25 02:01:35
【问题描述】:

我正在浏览使用这么多 shared_ptrs 的替代方法,并在评论部分找到了一个很好的回复:

您真的需要共享所有权吗? 如果你停下来想一想 分钟,我相信你能确定一个 对象的所有者,以及一些 它的用户,只会使用 它在所有者的一生中。所以 只需使其成为本地/成员对象 的所有者,并将引用传递给 那些需要使用它的人。

我很想这样做,但问题是拥有对象的定义现在需要首先完全定义拥有的对象。例如,假设我在 FooManager.h 中有以下内容:

class Foo; 
class FooManager
{
    shared_ptr<Foo> foo;
    shared_ptr<Foo> getFoo() { return foo; }
};

现在,根据上面的建议,FooManager.h 变成:

#include "Foo.h"
class FooManager
{
    Foo foo;
    Foo& getFoo() { return foo; }
};

我有两个问题。首先,FooManager.h 不再是轻量级的。每个包含它的 cpp 文件现在也需要编译 Foo.h。其次,我不再可以选择何时初始化 foo。它必须与 FooManager 同时初始化。我该如何解决这些问题?

【问题讨论】:

    标签: c++ initialization reference header-files smart-pointers


    【解决方案1】:

    您可以使用shared_ptr(或任何智能指针,甚至是哑指针)但没有共享所有权。

    例如

    class Foo; 
    class FooManager
    {
      private:
        shared_ptr<Foo> foo;
      public:
        Foo& getFoo() { return *foo; }
    };
    

    (这只是一个草图 - 你仍然需要一个 setFoo(),也许 getFoo() 应该返回一个 Foo *。但关键是你回到了轻量级,你可以控制何时创建 foo .)

    【讨论】:

    • 我要补充一点,在这种情况下,auto_ptr 具有灵活性和简单性的最佳组合,因为他不再需要引用计数功能。
    • 由于“有点奇怪”的复制语义,auto_ptr 是否被很好地理解为难以正确使用?
    【解决方案2】:

    如果您不需要shared_ptr 的共享所有权语义,请考虑使用不同的智能指针容器。

    对于您给出的示例(一个对象拥有所有权且没有所有权转让),boost::scoped_ptr 将是一个不错的选择。

    如果您不想使用 Boost,scoped_ptr 很容易实现——在 Boost 文档中有详细的描述,并且它的实现(在 boost/shared_ptr.hpp 中)很简单。

    【讨论】:

    • 是的,boost:scoped_ptr 的工作。随时通过void reset(T * p = 0); // never throws设置它。
    • @BillyONeal:确实如此:它抑制了隐式复制构造函数和赋值运算符,但不会阻止您实现用户定义的复制构造函数和赋值运算符。不过,如果您不需要共享所有权,这可能不是什么大问题。
    【解决方案3】:

    使用 pimpl 成语,不要再这么多内联了。

    FooManager.h:

    class Foo;
    
    class FooManager
    {
       struct Impl;
       Impl *m_impl;
    public:
       Foo& getFoo();
    
       FooManager();
       ~FooManager();
    };
    

    FooManager.cpp

    #include "Foo.h"
    #include "FooManager.h"
    
    struct FooManager::Impl
    {
       Foo* m_foo;
       int m_someothermember;
       FooManager::Impl() : m_foo(NULL), m_someothermember(0) {}
    };
    
    FooManager::FooManager() : m_impl(new FooManager::Impl())
    {}
    
    Foo& FooManager::getFoo()
    {
       // Lazy initialization
       if( !m_impl->m_foo ) {
          m_impl->m_foo = new Foo;
       }
       return *m_impl->m_foo;
     }
    
     FooManager::~FooManager()
     {
        delete m_impl->m_foo;
        delete m_impl;
     }
    

    由于您已经在使用 boost,我建议您使用 scoped_ptr 在您的实际代码中实现此功能,而不是像我在本示例中所做的那样手动管理内存。要记住的重要一点是,前向声明对引用的作用与对指针的作用一样好(这是它对shared_ptr 起作用的部分原因)。

    【讨论】:

    • pimpl 成语对解决方案至关重要吗?与标头中的scoped_ptr&lt;Foo&gt; m_foo(甚至只是Foo* m_foo)和cpp文件中的Foo&amp; getFoo() { if (!m_foo) m_foo.reset(new Foo); return *m_foo }相比,相关优势是什么?
    • 不,不是。 pimpl 成语是针对“减少标题膨胀”方面的,你不需要走这么远。
    猜你喜欢
    • 2012-11-01
    • 2014-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 2012-10-16
    相关资源
    最近更新 更多