【问题标题】:Can I use boost::make_shared with a private constructor?我可以将 boost::make_shared 与私有构造函数一起使用吗?
【发布时间】:2010-04-07 06:00:12
【问题描述】:

考虑以下几点:

class DirectoryIterator;

namespace detail {
    class FileDataProxy;

    class DirectoryIteratorImpl
    {
        friend class DirectoryIterator;
        friend class FileDataProxy;

        WIN32_FIND_DATAW currentData;
        HANDLE hFind;
        std::wstring root;

        DirectoryIteratorImpl();
        explicit DirectoryIteratorImpl(const std::wstring& pathSpec);
        void increment();
        bool equal(const DirectoryIteratorImpl& other) const;
    public:
        ~DirectoryIteratorImpl() {};
    };

    class FileDataProxy //Serves as a proxy to the WIN32_FIND_DATA struture inside the iterator.
    {
        friend class DirectoryIterator;
        boost::shared_ptr<DirectoryIteratorImpl> iteratorSource;
        FileDataProxy(boost::shared_ptr<DirectoryIteratorImpl> parent) : iteratorSource(parent) {};
    public:
        std::wstring GetFolderPath() const {
            return iteratorSource->root;
        }
    };
}

class DirectoryIterator : public boost::iterator_facade<DirectoryIterator, detail::FileDataProxy, std::input_iterator_tag>
{
    friend class boost::iterator_core_access;
    boost::shared_ptr<detail::DirectoryIteratorImpl> impl;
    void increment() {
        impl->increment();
    };
    bool equal(const DirectoryIterator& other) const {
        return impl->equal(*other.impl);
    };
    detail::FileDataProxy dereference() const {
        return detail::FileDataProxy(impl);
    };
public:
    DirectoryIterator() {
        impl = boost::make_shared<detail::DirectoryIteratorImpl>();
    };
};

看来 DirectoryIterator 应该可以调用boost::make_shared&lt;DirectoryIteratorImpl&gt;,因为它是DirectoryIteratorImpl 的朋友。但是,此代码无法编译,因为 DirectoryIteratorImpl 的构造函数是私有的。

由于这个类是DirectoryIterator 的客户端永远不应该接触的内部实现细节,如果我可以将构造函数保持为私有就好了。

这是我对make_shared 的根本误解,还是我需要将某种增强片段标记为friend 才能编译调用?

【问题讨论】:

  • 你确定你的 impl 指针需要 shared_ptr 吗? boost::scoped_ptr 通常更合适,并使事情变得更简单。如果您希望 DirectoryIterator 可复制并且副本应共享单个 impl 实例,则通常仅在这种情况下使用 Shared_ptr。在您发布的代码中,共享 impl 的副本似乎是一个错误。 Shared_ptr 用于当多个指针应该共享一个实例的所有权时。

标签: c++ boost shared-ptr make-shared


【解决方案1】:

您确实需要为此结交一些助推器朋友。基本上make_shared 正在调用构造函数,而这是在友元函数中完成的这一事实对编译器来说并不重要。

不过,好消息是make_shared 正在调用构造函数,而不是任何其他部分。所以只是让make_shared成为朋友就可以了……但这意味着任何人都可以创建shared_ptr&lt;DirectoryIteratorImpl&gt;……

【讨论】:

  • 嗯...这真烦人 :) 谢谢!
  • make_shared的问题是它分配了一块内存,然后使用了placementnew,所以它必须自己调用构造函数。我同意你的问题很烦人。
  • 这样做的问题是,如果您迁移到 TR1 或 C++0x,或者即使 boost 发布了更新,您也不能保证它仍然可以工作。
  • 我一直在思考这个问题......我突然意识到一切都很好。 DirectoryIteratorImpl 是一个私有类,它永远不会暴露在你的界面中,所以 FileDataProxyDirectoryIterator 中的前向声明就足够了。然后您可以将DirectoryIteratorImpl 标头与您的源文件(私有标头)放在一起,并将其构造函数公开。通常没有人应该有权访问此标头,即使他们这样做,他们也无法对此类的实例做任何事情,因为它永远不会出现在您的界面中,所以您很安全,问题解决了!跨度>
  • "不过好消息是 make_shared 正在调用构造函数,而不是任何其他部分。" 你怎么知道的?
【解决方案2】:

是否有充分的理由不使用旧的 shared_ptr 构造函数? (如果有的话,你可能想看看make_shared 的实现并去做)

DirectoryIterator()
   : impl( new detail::DirectoryIteratorImpl() )
{}

这样,对构造函数的调用是从已经是DirectoryIteratorImpl 的朋友的DirectoryIterator 类进行的,而无需为所有其他代码打开大门。

【讨论】:

  • 不,没有任何问题。但有人告诉我在 stackoverflow.com/questions/2569046/… 上使用 make_shared 。我现在所做的只是完全按照您的建议完成。 +1
  • make_shared 的内存分配效率更高...(碎片更少,速度更快)
  • 我知道内存碎片(你应该真正衡量一下),但我有时读到(实际上是在这里):为了性能而牺牲正确性的人都不值得,这是一个很好的座右铭。在最初的链接问题中,比利承认他不需要表演。如果构造函数是私有的,make_shared 不应该是朋友(这实际上接近于打破封装,允许任何人通过make_shared 构造对象)
  • 这就是为什么我在使用 shared_ptr 的构造函数时完全按照您的建议做了。但是你问我为什么使用 make_shared,我告诉你:)
【解决方案3】:

您可以将您的类拆分为接口部分和实现部分。 接口部分是公开的,实现部分可以有公开的构造函数。 但是,这意味着您必须使用虚拟继承。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-02
    • 2018-11-09
    • 1970-01-01
    • 1970-01-01
    • 2011-11-05
    相关资源
    最近更新 更多