我能使用boost::make_shared来调用私有构造函数吗?

7

考虑以下内容:

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,因为它是DirectoryIteratorImpl的友元。但是,由于DirectoryIteratorImpl的构造函数是私有的,所以此代码无法编译。
由于这个类是客户端不应该接触到的内部实现细节,因此如果我能保持构造函数私有,那就太好了。
这是我对make_shared的基本误解还是我需要将某些boost部分标记为友元才能使调用编译通过?

你确定需要使用shared_ptr来管理impl指针吗?通常情况下,boost::scoped_ptr更为适合且能使事情变得更简单。只有在想要DirectoryIterator可复制且副本共享单个impl实例的情况下,才会通常使用shared_ptr。在你发布的代码中,似乎副本共享一个impl是错误的。shared_ptr用于多个指针共享实例所有权的情况。 - Alan
3个回答

5
你确实需要为此制作一些boost代码。基本上,make_shared调用构造函数,而这是从友元函数中完成的,这对编译器并不重要。好消息是,make_shared只调用构造函数,而不涉及其他内容。因此,只需将make_shared设为友元即可...但这意味着任何人都可以创建一个shared_ptr<DirectoryIteratorImpl>...

1
嗯...这真是让人恼火 :) 谢谢! - Billy ONeal
1
这样做的问题在于,如果您迁移到TR1或C++0x,甚至是如果boost发布更新,您无法保证它仍然可以工作。 - dvide
“好消息是,make_shared 调用的是构造函数,而不是其他任何部分。” 你怎么知道的? - curiousguy
@curiousguy:当你有疑问时,查看实现。 - Matthieu M.
@MatthieuM。那么你只能得到关于这个特定库实现的一个版本的答案。任何未来的发布都可能打破你的假设。 - curiousguy
显示剩余4条评论

4

有没有不使用传统的shared_ptr构造函数的好理由?(如果有的话,您可能需要查看make_shared实现并进行操作)

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

这样,构造函数的调用就是从已经是DirectoryIteratorImpl友元的DirectoryIterator类中进行的,而不会为所有其他代码打开大门。


不,这没问题。但是我被告知在 https://dev59.com/2EzSa4cB1Zd3GeqPq_TJ#2569211 上使用 make_shared。目前我所做的就是按照你的建议简单地实现了。+1 - Billy ONeal
1
make_shared 在内存分配方面更加高效...(减少了碎片化,提高了速度) - Matthieu M.
1
我知道内存碎片的问题(你应该真正地测量一下),但我在 Stack Overflow 上读到过这样的话:为了性能而牺牲正确性的人将得不到两者,这是一个很好的座右铭。在原始链接的问题中,Billy 承认他不需要性能。如果构造函数是私有的,make_shared 不应该成为友元(这实际上非常接近通过允许任何人通过 make_shared 构造对象来破坏封装)。 - David Rodríguez - dribeas
这就是为什么我按照你建议的使用了shared_ptr的构造函数。但你问我为什么要使用make_shared,我现在告诉你 :) - Billy ONeal

0
你可以将你的类分为接口部分和实现部分。 接口部分是公开的,实现部分可以有公共构造函数。 然而,这意味着你必须使用虚继承。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接