2010-03-25 44 views
3

我浏览了一个替代使用这么多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同时初始化。我如何解决这些问题?

回答

3

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

E.g.

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

(这只是一个草图 - 你仍然需要一个setFoo(),也许还有的getFoo()应该返回一个Foo *但问题是,你又回到了重量轻,你可以控制的。当创建foo时)。

+0

我想补充一点,在这种情况下,auto_ptr具有灵活性和简单性的最佳组合,因为他不再需要引用计数功能。 – 2010-03-25 02:19:47

+1

由于“有点奇怪”的复制语义,是不是很好地理解了auto_ptr难以正确使用? – 2010-03-25 03:13:14

3

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

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

如果你不想使用Boost,scoped_ptr很容易实现 - 它在Boost文档中有很好的描述,它的实现(在boost/shared_ptr.hpp中)很简单。

+0

请记住,这使得'FooManager'不可复制。 – 2010-03-25 02:10:03

+0

是的,这是'boost:scoped_ptr'的工作。每当通过'void reset(T * p = 0)设置它; //从不抛出。 – 2010-03-25 02:13:19

+0

@BillyONeal:的确如此:它禁止隐式拷贝构造函数和赋值运算符,但它并不妨碍您实现用户定义的拷贝构造函数和赋值运算符。但是,如果你不需要共享所有权,这可能不会是一个大问题。 – 2010-03-25 02:14:57

2

使用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; 
} 

既然你已经使用升压,我会建议你使用scoped_ptr您的真实代码来实现这个,而不是手动管理记忆,就像我在这个例子中所做的一样。需要记住的重要一点是前向声明工作同样适用于引用,就像指针一样(这是它为shared_ptr工作的原因的一部分)。

+0

pimpl成语对解决方案至关重要吗?在头部和'Foo&getFoo(){if(!m_foo)m_foo.reset(new Foo)'范围内的scoped_ptr m_foo(甚至只是Foo * m_foo)有什么优势?返回* m_foo}'在cpp文件中? – Kyle 2010-03-25 04:31:39

+0

不,不是。 pimpl成语的目标是“减少标题膨胀”方面,你不需要走这么远。 – 2010-03-25 13:42:12