2015-01-05 2 views
0

Я предоставляю SDK своим пользователям, позволяя им писать DLL на C++ для расширения программного обеспечения.Предотвращение подкласса интерфейса абстрактного класса в C++

В заголовках SDK в основном содержатся определения класса интерфейса. Эти класса бывают двух типов:

  • Некоторые, которые пользователь должен создать подкласс и реализовать
  • некоторые, которые являются обертками для основных классов, принятый в приложении к функциям DLL в качестве указателей, которые затем могут быть использованы в качестве аргументов по коду DLL для вызова основных функций. Эти интерфейсы не должны подклассифицироваться пользователем и передаваться на основные функции, поскольку они ожидают определенного основного подкласса.

Я пишу в руководстве интерфейсы, которые не следует подклассифицировать и использовать только с помощью указателей на объекты, предоставляемые приложением. Но в некоторых местах слишком сложно заклассифицировать их в SDK, если вы не читаете руководство.

Можно ли предотвратить подкласс некоторых интерфейсов в заголовках SDK?

+0

[C++ 11 имеет последнее ключевое слово] (http://en.cppreference.com/w/cpp/language/final) – Borgleader

+0

... аннотация final? – Wintermute

+0

Основная проблема заключается в том, что вы * делаете * необходимость подкласса в своей собственной реализации. Таким образом, в вашей реализации вам нужно будет увидеть другое объявление, чем ваши вызывающие. (например, 'final' или private CTor). Это, безусловно, возможно для большинства реализаций с помощью reinterpret_cast, но я бы посоветовал это сделать, поскольку это был бы уродливый взлом с предельными преимуществами. – peterchen

ответ

0

Пока клиент не должен использовать указатель для чего-либо, кроме , передавая его обратно в вашу DLL, вы можете просто использовать объявление вперед; вы не можете получить неполный тип. (В тот момент, когда я столкнулся с аналогичным случаем , я отправился целым свиным гриппом и разработал специальный тип обертки на основе void*. В коде интерфейса много отливок, но , что клиент не может сделать ничего, кроме как пройти значение возвращается к me.)

Если классы, о которых идет речь, реализуют интерфейс, который также должен использовать клиент , существует два решения. Первое заключается в том, чтобы изменить это: , заменяя каждую из функций-членов свободной функцией, которая принимает указатель на тип и просто предоставляет объявление вперед. второй использовать что-то вроде:

class InternallyVisibleInterface; 

class ClientVisibleInterface 
{ 
private: 
    virtual void doSomething() = 0; 
    ClientVisibleInterface() = default; 
    friend class InternallyVisibleInterface; 
protected:  // Or public, depending on whether the client should 
       // be able to delete instances or not. 
    virtual ~ClientVisibleInterface() = default; 
public: 
    void something(); 
}; 

и в вашей DLL:

class InternallyVisibleInterface : public ClientVisibleInterface 
{ 
protected: 
    InternallyVisibleInterface() {} 

    // And anything else you need. If there is only one class in 
    // your application which should derive from the interface, 
    // this is it. If there are several, they should derive from 
    // this class, rather than ClientVisibleInterface, since this 
    // is the only class which can construct the 
    // ClientVisibleInterface base class. 
}; 

void ClientVisibleInterface::something() 
{ 
    assert(dynamic_cast<InternallyVisibleInterface*>(this) != nullptr); 
    doSomething(); 
} 

Это обеспечивает два уровня защиты: во-первых, хотя вывод непосредственно из ClientVisibleInterface возможно, это невозможно для результирующий класс должен иметь конструктор, и поэтому он не может быть создан в . А во-вторых, если код клиента как-то обманывает, будет ошибка выполнения, если он это сделает.

Вам, вероятно, не нужна защита; одному или другому должно соответствовать . Частный конструктор приведет к ошибке времени компиляции, , а не во время выполнения. С другой стороны, без этого, вы не должны даже указать имя InternallyVisibleInterface в распределенных заголовках .

0

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

ИМХО лучшее, что вы можете сделать, это определить границы между приложением ядра и библиотеками DLL расширения и обеспечением того, чтобы предметы, полученными из этих библиотек DLL является или правильным классом, и прервать с отличительным сообщением, если они не являются.

Использование RTTI и typeid обычно неодобрение, потому что, как правило, признак плохого дизайна ООП: в нормально прецеденте, вызов виртуального метода достаточно иметь правильный код вызов. Но я думаю, что это можно смело рассматривать в вашем случае использования.