Умный указатель с пользовательским удалителем в качестве члена класса - PullRequest
2 голосов
/ 06 марта 2020

Моя ситуация такова: у меня есть файл .dll и заголовок .h с чисто виртуальным объявлением class MainWindow. В заголовочном файле есть два определения типов:

typedef MainWindow* (*CREATE_INTERFACE)();
typedef void (*DELETE_INTERFACE)(MainWindow* Window);

, которые позволяют мне создавать и удалять объекты того же типа, что и виртуальный класс.

Я бы хотел, чтобы поддержка загрузки для эту динамическую c библиотеку, которая будет определена в моем классе, назовем ее class Loader. Мне бы хотелось, чтобы один из моих учеников был умным указателем, определяемым следующим образом:

std::unique_ptr <MainWindow,DELETE_INTERFACE> UI_Driver;

Упрощенный код ниже (без обработки ошибок):

class Loader
{
    private:
    HINSTANCE DllHandle;
    CREATE_INTERFACE FactoryFunction;
    DELETE_INTERFACE CustomDeleterFunction;
    std::unique_ptr<MainWindow,DELETE_INTERFACE> UI_Driver;

    std::unique_ptr<MainWindow,DELETE_INTERFACE> LoadExternalLibrary()
    {
        std::wstring WideFileName=L"MainWindow.dll";
        std::string FileName(std::begin(WideFileName),std::end(WideFileName));
        this->DllHandle=::LoadLibrary(WideFileName.c_str());
        // Get the function from the DLL
        FactoryFunction=reinterpret_cast<CREATE_INTERFACE>(::GetProcAddress(DllHandle,"Create"));
        CustomDeleterFunction=(DELETE_INTERFACE)GetProcAddress(DllHandle,"Delete");
        return std::unique_ptr<MainWindow,DELETE_INTERFACE>(FactoryFunction(),CustomDeleterFunction);
     };

public:

     UI_Service() : UI_Driver(LoadExternalLibrary())
     {

     }

     ~UI_Service()
     {
         if(UI_Driver)
             ::FreeLibrary(this->DllHandle);
     }

     void ShowWindow()
     {
        UI_Driver->Show();
     }
};

Код компилируется правильно, загрузка функций из библиотеки .dll также успешна. MainWindow, определенный в заголовке, имеет метод Show(), который dr aws пользовательский интерфейс. Если я попытаюсь вызвать его, как описано выше, с помощью метода ShowWindow() из моего class Loader, окно не появится.

int main()
{
 Loader MyLoader;
 MyLoader.ShowWindow(); // <- window does not appear, program crashes
}

НО появляется, если я вызываю этот метод сразу после создания указателя в методе LoadExternalLibrary(), как показано ниже:

, поэтому вместо:

return std::unique_ptr<Eol_MainWindow_Driver_Generic,DELETE_INTERFACE>(FactoryFunction(),CustomDeleterFunction);

i можно написать:

std::unique_ptr<Eol_MainWindow_Driver_Generic,DELETE_INTERFACE> UI(FactoryFunction(),CustomDeleterFunction);
UI->Show(); // <- Window appears
return UI; // <- It will never happen because the Show() is blocking the thread

Что происходит? Почему созданный unique_ptr с пользовательским средством удаления перестает работать после копирования?

EDIT : я нашел ответ частично -
FactoryFunction и CustomDeleterFunction работают только в функции LoadExternalDll прицелы. Понятия не имею почему.

1 Ответ

0 голосов
/ 06 марта 2020

Мне лично кажется, что это не проблема, связанная с умным указателем с пользовательской функцией удаления. Потому что существует концепция, называемая оптимизацией возвращаемого значения, которая происходит, когда функция возвращает значение std::unique_ptr по значению, а копирование / перемещение не происходит. Вы можете прочитать больше здесь Copy_elision .

Чтобы показать это, я создал небольшой пример с std::unique_ptr с пользовательской функцией удаления.

#include <iostream>

#include <memory>
#include <vector>

using customDeleteType = void(*)(std::vector<int*>*);

class Demo{
public:
    Demo();
    ~Demo();

    void printVector() const;
private:
    std::unique_ptr<std::vector<int*>, customDeleteType> populateVector() const;

    std::unique_ptr<std::vector<int*>, customDeleteType> vec;
};

Demo::Demo(): vec(populateVector()){

}

Demo::~Demo(){

}

void Demo::printVector() const{
    for(int* i: *vec){
        std::cout<< *i<< " ";
    }
    std::cout<< '\n';
}

std::unique_ptr<std::vector<int*>, customDeleteType> Demo::populateVector() const
{
    return std::unique_ptr<std::vector<int*>, customDeleteType>(
                new std::vector<int*>{new int{0}, new int{1}},
                [](std::vector<int*>* v){ for(int* i: *v){delete i;} delete v;}
                );
}

int main(int , char *[]){
    Demo d;
    d.printVector();
}

выход: 0 1

...