Использование PIMPL с синглтонами и auto_ptr

У меня есть синглтон с рядом статических методов и методов экземпляра. Я хотел бы экспортировать его в DLL, чтобы я мог использовать его в другом месте. Однако это вызвало предупреждение компилятора, потому что я не экспортировал закрытые данные-члены класса.

Итак, в своем исследовании я наткнулся на идиому PIMPL и увидел, что ее можно использовать для того, чтобы делать именно то, что я хочу делать. Пожалуйста, давайте воздержимся от аргумента «Синглтоны — это зло/анти-паттерны». В моем случае это имеет смысл, потому что мне нужен класс менеджера для всех моих подобъектов. Для краткости я собираюсь сократить некоторые другие методы в классе, но оставлю достаточно для общей идеи.

Идея пустого деструктора HIDDevice в файле HIDDeviceImpl CPP взята из этой статьи Али Чехрели.

HIDDevice.hpp

class HIDDeviceImpl; // Forward Declaration

class HIDDevice
{
public:

    static HIDDevice* getDevice(unsigned short vendorID, unsigned short productID);

    int writeData(const unsigned char *data, int length);


    int readData(unsigned char *data, int length);

    ~HIDDevice(); // Note public destructor declared in HIDDevice.hpp

private:

    std::unique_ptr<HIDDeviceImpl> m_pImpl; // pointer to the implemetation
};

HIDDevice.cpp

#include "HIDDeviceImpl.hpp"
#include "HIDDevice.hpp"

HIDDevice* HIDDevice::getDevice(unsigned short vendorID, unsigned short productID)
{
    return HIDDeviceImpl::getDevice(vendorID, productID);
}

int HIDDevice::writeData(const unsigned char *data, int length)
{
    return m_pImpl->writeData(data, length);
}


int HIDDevice::readData(unsigned char *data, int length)
{
    return m_pImpl->readData(data, length);
}

HIDDeviceImpl.hpp

#include "HIDDevice.hpp"

class HIDDeviceImpl : public HIDDevice
{
public:

    static HIDDeviceImpl* getDevice(unsigned short vendorID, unsigned short productID);

    int writeData(const unsigned char *data, int length);

    int readData(unsigned char *data, int length);

private:
    // some private static and private member functions and data
    // private constructor and destructor
};

HIDDeviceImpl.hpp

#include "HIDDeviceImpl.hpp"

/** Non-member Static Data Definitions **/
/** internal map used to store all HIDDevice objects */
static std::map<std::string, HIDDeviceImpl*> m_hidDevices;

HIDDeviceImpl* HIDDeviceImpl::getDevice(unsigned short vendorID, unsigned short productID) 
{
    //implementation
}

int HIDDeviceImpl::writeData(const unsigned char *data, int length)
{
    //implementation
}

int HIDDeviceImpl::readData(unsigned char *data, int length)
{
    //implementation
}

HIDDeviceImpl::HIDDeviceImpl(unsigned short vendorID, unsigned short productID, std::string serialNumber)
{
    // implementation
}

HIDDeviceImpl::~HIDDeviceImpl()
{
    // implementation
}

// Note the HIDDevice destructor is declared in the HIDDeviceImpl file.
HIDDevice::~HIDDevice()
{
    // intentionally left blank
}

Теперь ошибка, которую это генерирует, следующая:

error C2248: 'HIDDeviceImpl::~HIDDeviceImpl' : cannot access private member declared in class 'HIDDeviceImpl'

Это связано с auto_ptr в HIDDevice.hpp.


person audiFanatic    schedule 15.07.2016    source источник
comment
Хотя std::auto_ptr была хорошей первой попыткой внедрить интеллектуальные указатели в мир C++, она оказалась ошибочной, а std::auto_ptr устарела, начиная с C++11, и будет удалена в C++17. Либо используйте ручное управление указателями, либо используйте новый std::unique_ptr.   -  person Some programmer dude    schedule 15.07.2016
comment
unique_ptr и связанные с ним указатели были созданы для замены сломанного auto_ptr. Обратите внимание, статья 2002 года.   -  person SurvivalMachine    schedule 15.07.2016
comment
Класс pImpl в идиоме Pimpl не является производным от общедоступного класса. Фактически, общедоступный класс не должен появляться нигде в pImpl, потому что в противном случае у вас будет циклическая ссылка.   -  person KABoissonneault    schedule 15.07.2016
comment
@KABoissonneault Да, это то, что я заметил. Итак, я думаю, HIDDevice также должен быть синглтоном?   -  person audiFanatic    schedule 15.07.2016
comment
@JoachimPileborg Спасибо за предупреждение, я изменю его.   -  person audiFanatic    schedule 15.07.2016
comment
@audiFanatic Только HIDDeviceImpl должен быть синглтоном. HIDDevice в лучшем случае может быть просто пространством имен, только со статическими функциями.   -  person KABoissonneault    schedule 15.07.2016
comment
@KABoissonneault Итак, я думаю, мне никогда не придется возвращать HIDDevice или HIDDeviceIMpl?   -  person audiFanatic    schedule 15.07.2016
comment
@audiFanatic Точно. Оставьте только writeData и readData в HIDDevice и заставьте реализацию этих функций вызывать эти функции в синглтоне внутри. Гораздо меньше проблем для пользователя   -  person KABoissonneault    schedule 15.07.2016


Ответы (1)


Почему бы просто не сделать то, что говорит вам сообщение об ошибке, и сделать деструктор общедоступным? Таким образом, внутренности std::unique_ptr получат к нему доступ.

person Martin Bonner supports Monica    schedule 15.07.2016
comment
Потому что я не хочу, чтобы объект был преждевременно уничтожен ошибкой программиста. - person audiFanatic; 15.07.2016
comment
@audiFanatic Он не будет уничтожен раньше из-за ошибки программиста, потому что m_pImpl доступен только из HIDDevice (потому что он частный) - person KABoissonneault; 15.07.2016
comment
@KABoissonneault Правда, я думаю, сначала я попытаюсь реализовать настоящий PIMPL (согласно вашим другим комментариям), а затем попробую. - person audiFanatic; 15.07.2016