У меня есть библиотека QT, и я хочу импортировать ее в другой проект.
Теперь, когда я хочу, чтобы, даже когда я изменял библиотеку, другой проект не нужно было снова компилировать, я начал использовать QLibrary.
Но … я не могу импортировать класс. Или, что лучше, я могу импортировать класс, но не могу получить доступ к его методам.
Это пример, который я сделал.
Это объявление класса:
class TESTDLL_LIBSHARED_EXPORT TestDLL_lib
{
public:
TestDLL_lib();
int a;
int b;
int c;
int getValues();
};
и это реализация:
#include "testdll_lib.h"
TestDLL_lib::TestDLL_lib()
{
a = 10;
b = 20;
c = 30;
}
int TestDLL_lib::getValues()
{
return a+b+c;
}
extern "C" TESTDLL_LIBSHARED_EXPORT TestDLL_lib* create_TestDLL_lib()
{
return new TestDLL_lib();
}
пока это основной файл, в другом проекте:
#include <testdll_lib.h>
#include <QDebug>
#include <QLibrary>
int main(int argc, char *argv[])
{
QLibrary library("TestDLL_lib");
if (library.load())
{
typedef TestDLL_lib* (*create_TestDLL_lib_fun)();
create_TestDLL_lib_fun create_TestDLL_lib = (create_TestDLL_lib_fun)library.resolve("create_TestDLL_lib");
if (create_TestDLL_lib)
{
TestDLL_lib *myClassInstance = create_TestDLL_lib();
if (myClassInstance)
{
//qDebug() << QString::number(myClassInstance->getValues());
qDebug() << QString::number(myClassInstance->a) + " " + QString::number(myClassInstance->b) + " " + QString::number(myClassInstance->c);
}
}
library.unload();
}
}
Теперь я могу получить доступ ко всем значениям данных (a
, b
, c
) объекта myClassInstance
(и, если я изменю их в DLL, они также изменятся в программе без перестройки), но я не могу позвонить myClassInstance->getValues()
потому что я получаю
main.obj:-1: error: LNK2001: unresolved external symbol "__declspec(dllimport) public: int __thiscall TestDLL_lib::getValues(void)" (__imp_?getValues@TestDLL_lib@@QAEHXZ)
Как я могу решить это? Можно ли вызывать методы из импортированных классов?
Спасибо..
Вы не можете вызывать методы для классов, импортированных во время выполнения. Это потому, что компилятор связывает эти вызовы во время компиляции, а не во время выполнения (что он не могу сделать). Выход из ситуации предоставлен нашим добрым старым другом, vtable:
Ты можешь позвонить virtual
методы в классах, реализующих интерфейс (интерфейс не «импортируется» во время выполнения). Это означает определить класс, определяющий интерфейс, используя virtual
(возможно, чисто виртуальные) методы. TestDLL_lib
затем наследовал бы этот интерфейс, реализуя методы. Вы бы ссылались на TestDLL_lib
через этот интерфейс и вызывать методы через этот интерфейс, эффективно вызывая их через виртуальную таблицу интерфейса, которая «заменяется» TestDLL_lib
с vtable.
Не забудьте сделать свой д’тор virtual
и добавить virtual
дтор до интерфейса. Если вы этого не сделаете, вы не можете безопасно delete
экземпляр через указатель интерфейса.
Я мог бы также объяснить, почему вы можете получить доступ к членам, но не вызывать функции для «импортированных» классов. Доступ к членам осуществляется по расположению в памяти, а расположение в памяти определяется только компилятором. Таким образом, компилятор генерирует код для доступа к членам, даже не ссылаясь на символы классов (методы и т. Д.). Это в свою очередь приводит к отсутствию зависимости от связей. Однако обратите внимание, что вам нужно будет перекомпилировать DLL и приложение, используя DLL, если вы измените класс, например добавление или удаление члена, так как это меняет структуру памяти.
class TestInterface
{
public:
virtual ~TestInterface()
{
}
virtual int getValues() = 0;
}
class TESTDLL_LIBSHARED_EXPORT TestDLL_lib : public TestInterface
{
public:
TestDLL_lib();
virtual ~TestDLL_lib();
int a;
int b;
int c;
int getValues() override; // MSVC may not support "override"};
// return pointer to interface!
// TestDLL_lib can and should be completely hidden from the application
extern "C" TESTDLL_LIBSHARED_EXPORT TestInterface *create_TestDLL_lib()
{
return new TestDLL_lib();
}