2010-04-07 3 views
7

Рассмотрим следующий пример:Могу ли я использовать boost :: make_shared с частным конструктором?

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. Однако этот код не компилируется, потому что конструктор для DirectoryIteratorImpl является закрытым.

Поскольку этот класс является внутренней деталью реализации, которую клиенты DirectoryIterator никогда не должны касаться, было бы неплохо, если бы я мог сохранить конструктор закрытым.

Является ли это моим основным недоразумением в отношении make_shared или мне нужно отметить какой-то элемент повышения, как friend для того, чтобы получить возможность комментировать?

+0

Вы уверены, что вам нужен shared_ptr для вашего указателя impl? boost :: scoped_ptr обычно более уместен и делает вещи намного проще. Shared_ptr обычно будет использоваться только в этом случае, если вы хотите, чтобы DirectoryIterator был скопирован и что копии должны делиться одним экземпляром impl. В коде, который вы опубликовали, похоже, что копии, связанные с имплантом, будут ошибкой. Shared_ptr - это когда несколько указателей должны делиться собственностью экземпляра. – Alan

ответ

5

Вам действительно нужно сделать для этого дополнительный кусок. В основном make_shared вызывает конструктор, и тот факт, что это делается из функции друга, не имеет значения для компилятора.

Хорошая новость заключается в том, что make_shared вызывает конструктор, а не любую другую деталь. Так что просто делать make_shared друг будет работать ... Однако это означает, что любой может тогда создать shared_ptr<DirectoryIteratorImpl> ...

+1

Хм ... это досадно, черт возьми :) Спасибо! –

+0

Проблема 'make_shared' заключается в том, что он выделяет один блок памяти, а затем использует место размещения' new', поэтому он должен сам вызвать конструктор. Я согласен, что это раздражает вашу проблему. –

+1

Проблема с этим заключается в том, что вы переходите на TR1 или C++ 0x, или даже если форс-релиз обновляет, у вас нет гарантии, что он все равно будет работать. – dvide

4

Есть ли веская причина не использовать старый добрый shared_ptr конструктор? (Если есть один, вы можете захотеть взглянуть на make_shared реализации и сделать это)

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

Таким образом, вызов конструктора сделан из DirectoryIterator класса, который уже друг DirectoryIteratorImpl без открытия дверь для всего остального кода.

+0

Нет, нет ничего плохого в этом. Но мне сказали использовать 'make_shared' на http://stackoverflow.com/questions/2569046/is-there-a-way-to-increase-the-efficiency-of-shared-ptr-by-storing-the- справка/2569211 # 2569211. То, что я сделал сейчас, просто делается именно так, как вы предлагаете. +1 –

+1

'make_shared' более эффективен в распределении памяти ... (меньше фрагментации, большей скорости) –

+1

Я знаю о фрагментации памяти (вы действительно должны усомниться), но я когда-то читал (на самом деле здесь в SO): * Он который жертвует правильностью для исполнения, не заслуживает ни *, что является хорошим девизом. И в исходном связанном вопросе Билли признает, что ему не нужна производительность. Если конструктор является закрытым, 'make_shared' не должен быть другом (который действительно близок к разрыву инкапсуляции, позволяя кому-либо построить объект через' make_shared') –

0

Вы можете разделить свой класс на часть интерфейса и часть реализации. Интерфейсная часть обнародована, а часть реализации может иметь общедоступные конструкторы. Однако это означает, что вам нужно использовать виртуальное наследование.