Функции C++ __declspec(dllexport) не могут получить доступ к переменным экземпляра

Я пытаюсь защитить некоторый код C++, экспортируя его как DLL (в Windows/VS 2010).

В приведенном ниже примере var установлен в конструкторе суперкласса, и отладчик показывает, что он определенно установлен для ссылки на что-то.

Тест создается в коде, использующем библиотеку DLL, в которой содержится класс Test.

Но когда go вызывается из экземпляра теста (вызывается из DLL, но вызывающий метод вызывается потребителем DLL), var является нулевым указателем (его значение равно 0).

Это упрощение, поскольку мне не разрешено делиться фактическим кодом.

//Headers

class Base {
  public:
    __declspec(dllexport) Base();      
  private:
    Foo* var;
};

class Test : Base {
public:
  __declspec(dllexport) Test();
  __declspec(dllexport) void go();
private:
};

//Body

Base::Base() {
  var = new Foo();
}

Test::Test() : Base() {
}

void Test::go() {
  var->do_something();
}

В коде потребления заголовок

class Base {
public:
  __declspec(dllimport) Base();
}; 

class Test {
public:
  __declspec(dllimport) Test();
  __declspec(dllimport) void go();
};

Фактический код намного сложнее, но я был бы признателен, если бы кто-нибудь мог сказать мне, существуют ли известные ограничения на переменные экземпляра с dllexport, или более вероятно, что я вызываю метод для нулевого указателя для Test, или возможно, это проблема dllexport и наследования. Этот код работал до того, как я разделил код потребителя, и код DLL был в том же проекте, он сломался только после его разделения, функции dllexporting/dllimporting, которые я хочу представить во втором наборе заголовков, используемых потребителем.


person Paul Ridgway    schedule 08.02.2012    source источник


Ответы (3)


Когда вы передаете Test по значению из одного места в другое в «потребляющем коде», вы вызовете slicing, поскольку клиентский код не знает о переменной и вычисляет неправильный размер для класса Test.

Чтобы решить эту проблему, вы также должны объявить переменную в клиентском коде или, в качестве альтернативы, вы можете предоставить какую-либо статическую фабричную функцию и разрешить клиентскому коду передавать указатели только на Test, чтобы избежать нарезки.

person Seth Carnegie    schedule 08.02.2012
comment
Спасибо, я обновил вопрос, потому что понял, что в миксе тоже есть наследование. Я попробовал __declspec(dllexport) Foo* var; но я получил модификаторы __declspec, недействительные для этого объявления - person Paul Ridgway; 09.02.2012
comment
@PaulRidgway ах, извините, я ошибся, просто добавьте Foo* var к Base клиента (или, может быть, вам даже сойдет с рук void* var, если sizeof(void*) == sizeof(Foo*)) - person Seth Carnegie; 09.02.2012

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

  1. Предоставьте статическую функцию createTest, которая создает экземпляр вашего класса (фабричный метод). Лучше всего здесь предоставить чистый интерфейс (без переменной экземпляра).

  2. Если вы хотите скрыть только определенную часть своего класса, вы можете использовать идиому pimpl (статья в Википедии ).

person rasmus    schedule 08.02.2012

Есть ли причина не использовать:

#ifdef IN_FOO_PROJECT
# define fooEXPORT __declspec(dllexport)
#else
# define fooEXPORT __declspec(dllimport)
#endif

class fooEXPORT exportClass
{
public:
  void function( void );
  Foo * var;
}

Если вы хотите скрыть свой класс (или часть своего класса), вы можете использовать его как частный член:

#ifdef IN_FOO_PROJECT
# define fooEXPORT __declspec(dllexport)
#else
# define fooEXPORT __declspec(dllimport)
#endif

class classToHide;

class fooEXPORT exportClass
{
public:
  void function( void );
  classToHide * var;
}

И в КП:

#include "exportClass.h"
#include "classToHide.h"

void exportClass::function( void )
{
  var->function();
}
person Naszta    schedule 09.02.2012
comment
Существует много методов исходного класса (ов), которые я хочу скрыть, поэтому не могу экспортировать и импортировать все это. Я хочу экспортировать подмножество методов, которые, в свою очередь, вызывают другие внутренние и внешние методы, некоторые общедоступные и защищенные, доступ к которым осуществляется внутри DLL, но не снаружи. - person Paul Ridgway; 09.02.2012