Как настроить класс, представляющий интерфейс? Это просто абстрактный базовый класс?
c++
inheritance
interface
abstract-class
pure-virtual
Аарон Фишер
источник
источник
Ответы:
Чтобы расширить ответ с помощью bradtgmurray , вы можете сделать одно исключение из списка чисто виртуальных методов вашего интерфейса, добавив виртуальный деструктор. Это позволяет передавать владение указателем другой стороне, не раскрывая конкретный производный класс. Деструктор не должен ничего делать, потому что в интерфейсе нет конкретных членов. Может показаться противоречивым определение функции как виртуальной, так и встроенной, но, поверьте мне, это не так.
Вам не нужно включать тело для виртуального деструктора - оказывается, у некоторых компиляторов возникают проблемы с оптимизацией пустого деструктора, и вам лучше использовать значение по умолчанию.
источник
=0
) деструктор с телом. Преимущество здесь в том, что теоретически компилятор может увидеть, что vtable не имеет допустимых членов, и вообще отказаться от него. В случае виртуального деструктора с телом указанный деструктор может быть вызван (виртуально), например, в середине конструкции с помощьюthis
указателя (когда сконструированный объект все еще имеетParent
тип), и поэтому компилятор должен предоставить действительную vtable. Поэтому, если вы не вызываете виртуальные деструкторы напрямуюthis
во время создания :), вы можете сэкономить на размере кода.override
ключевое слово для проверки аргументов во время компиляции и проверки типа возвращаемого значения. Например, в декларации о детяхvirtual void OverrideMe() override;
Сделайте класс с чисто виртуальными методами. Используйте интерфейс, создав другой класс, который переопределяет эти виртуальные методы.
Чисто виртуальный метод - это метод класса, который определяется как виртуальный и присваивается 0.
источник
override
в C ++ 11Причина, по которой у вас есть специальная категория типов интерфейса в дополнение к абстрактным базовым классам в C # / Java, заключается в том, что C # / Java не поддерживает множественное наследование.
C ++ поддерживает множественное наследование, поэтому специальный тип не требуется. Абстрактный базовый класс без неабстрактных (чисто виртуальных) методов функционально эквивалентен интерфейсу C # / Java.
источник
Thread
экземпляром. Многократное наследование может быть как плохим дизайном, так и композицией. Все зависит от случая.В C ++ нет понятия «интерфейс» как такового. AFAIK, интерфейсы были впервые введены в Java, чтобы обойти отсутствие множественного наследования. Эта концепция оказалась весьма полезной, и тот же эффект может быть достигнут в C ++ с помощью абстрактного базового класса.
Абстрактный базовый класс - это класс, в котором хотя бы одна функция-член (метод в языке Java) является чисто виртуальной функцией, объявленной с использованием следующего синтаксиса:
Абстрактный базовый класс не может быть создан, т. Е. Вы не можете объявить объект класса A. Вы можете получить классы только из A, но любой производный класс, который не обеспечивает реализацию
foo()
, также будет абстрактным. Чтобы перестать быть абстрактным, производный класс должен предоставлять реализации для всех чисто виртуальных функций, которые он наследует.Обратите внимание, что абстрактный базовый класс может быть не просто интерфейсом, поскольку он может содержать члены-данные и функции-члены, которые не являются чисто виртуальными. Эквивалентом интерфейса был бы абстрактный базовый класс без каких-либо данных только с чисто виртуальными функциями.
И, как отметил Марк Рэнсом, абстрактный базовый класс должен обеспечивать виртуальный деструктор, как и любой базовый класс.
источник
Насколько я мог проверить, очень важно добавить виртуальный деструктор. Я использую объекты, созданные
new
и уничтоженныеdelete
.Если вы не добавите виртуальный деструктор в интерфейс, то деструктор унаследованного класса не вызывается.
Если вы запустите предыдущий код без
virtual ~IBase() {};
, вы увидите, что деструкторTester::~Tester()
никогда не вызывается.источник
Мой ответ в основном такой же, как и у других, но я думаю, что есть еще две важные вещи:
Объявите виртуальный деструктор в вашем интерфейсе или создайте защищенный не виртуальный, чтобы избежать неопределенного поведения, если кто-то пытается удалить объект типа
IDemo
.Используйте виртуальное наследование, чтобы избежать проблем с множественным наследованием. (При использовании интерфейсов чаще встречается множественное наследование.)
И, как и другие ответы:
Используйте интерфейс, создав другой класс, который переопределяет эти виртуальные методы.
Или
А также
источник
В C ++ 11 вы можете легко избежать наследования:
В этом случае интерфейс имеет ссылочную семантику, т.е. вы должны убедиться, что объект переживает интерфейс (также возможно создание интерфейсов с семантикой значения).
Эти типы интерфейсов имеют свои плюсы и минусы:
Наконец, наследование является корнем зла в разработке сложных программ. В семантике значений Sean Parent и основанном на понятиях полиморфизме (настоятельно рекомендуется, там объясняются лучшие версии этой методики) изучается следующий случай:
Скажем, у меня есть приложение, в котором я работаю с моими формами полиморфно, используя
MyShape
интерфейс:В вашем приложении вы делаете то же самое с различными формами, используя
YourShape
интерфейс:Теперь скажите, что вы хотите использовать некоторые формы, которые я разработал в вашем приложении. Концептуально наши фигуры имеют одинаковый интерфейс, но чтобы мои фигуры работали в вашем приложении, вам необходимо расширить мои фигуры следующим образом:
Во-первых, изменение моих форм может оказаться невозможным вообще. Более того, множественное наследование ведет к коду спагетти (представьте, что третий проект, использующий
TheirShape
интерфейс ... что произойдет, если они также вызовут свою функцию рисованияmy_draw
?).Обновление: есть пара новых ссылок о полиморфизме, не основанном на наследовании:
источник
Circle
класс - плохой дизайн. Вы должны использоватьAdapter
шаблон в таких случаях. Извините, если это будет звучать немного резко, но попробуйте использовать некоторую реальную библиотеку, какQt
прежде, чем выносить суждения о наследовании. Наследование делает жизнь намного проще.Adapter
шаблона? Мне интересно увидеть его преимущества.Square
его там еще нет? Предвидение? Вот почему это оторвано от реальности. И на самом деле, если вы решите положиться на библиотеку «MyShape», вы можете использовать ее интерфейс с самого начала. В примере фигур есть много глупостей (одна из которых состоит в том, что у вас есть двеCircle
структуры), но адаптер будет выглядеть примерно так -> ideone.com/UogjWkВсе хорошие ответы выше. Еще одна вещь, которую вы должны иметь в виду - у вас также может быть чистый виртуальный деструктор. Разница лишь в том, что вам все еще нужно это реализовать.
Смущенный?
Основная причина, по которой вы хотите это сделать, заключается в том, что вы хотите предоставить методы интерфейса, как я, но сделать переопределение их необязательным.
Чтобы сделать класс интерфейсным классом, необходим чисто виртуальный метод, но все ваши виртуальные методы имеют реализации по умолчанию, поэтому единственный метод, который остается сделать чисто виртуальным, - это деструктор.
Переопределение деструктора в производном классе не представляет особой проблемы - я всегда переопределяю деструктор, виртуальный или нет, в своих производных классах.
источник
Если вы используете компилятор Microsoft C ++, вы можете сделать следующее:
Мне нравится этот подход, потому что он приводит к гораздо меньшему коду интерфейса, а размер сгенерированного кода может быть значительно меньше. Использование novtable удаляет все ссылки на указатель vtable в этом классе, поэтому вы никогда не сможете создать его экземпляр напрямую. Смотрите документацию здесь - novtable .
источник
novtable
по стандартуvirtual void Bar() = 0;
= 0;
которое я добавил). Прочтите документацию, если вы ее не понимаете.= 0;
и предположил, что это был просто нестандартный способ сделать то же самое.Небольшое дополнение к тому, что там написано:
Во-первых, убедитесь, что ваш деструктор также является чисто виртуальным
Во-вторых, вы можете захотеть наследовать виртуально (а не нормально), когда вы реализуете, просто для хороших мер.
источник
Вы также можете рассмотреть классы контрактов, реализованные с помощью NVI (Non Virtual Interface Pattern). Например:
источник
Я все еще новичок в разработке C ++. Я начал с Visual Studio (VS).
Тем не менее, никто не упоминал
__interface
в VS (.NET) . Я не очень уверен, что это хороший способ объявить интерфейс. Но, похоже, предусматривают дополнительное правоприменение (упомянуто в документах ). Так что вам не нужно явно указыватьvirtual TYPE Method() = 0;
, так как он будет автоматически преобразован.Если у кого-то есть что-нибудь интересное об этом, пожалуйста, поделитесь. :-)
Спасибо.
источник
Несмотря на то, что
virtual
это де-факто стандарт для определения интерфейса, давайте не будем забывать о классическом C-подобном шаблоне, который поставляется с конструктором в C ++:Преимущество этого заключается в том, что вы можете перепривязывать события во время выполнения без необходимости повторного конструирования вашего класса (поскольку C ++ не имеет синтаксиса для изменения полиморфных типов, это обходной путь для классов хамелеонов).
Советы:
click
вашего потомка.protected
члена и иметьpublic
ссылку и / или получатель.if
s и состояния в вашем коде, это может быть быстрее, чемswitch()
es илиif
s (изменение ожидается примерно через 3-4if
с, но всегда измеряйте в первую очередь.std::function<>
более указателей на функции, вы могли бы быть в состоянии управлять всеми данными объекта внутриIBase
. С этого момента вы можете иметь схему значений дляIBase
(например,std::vector<IBase>
будет работать). Обратите внимание, что это может быть медленнее в зависимости от вашего компилятора и кода STL; также, что текущие реализацииstd::function<>
имеют тенденцию иметь накладные расходы по сравнению с указателями функций или даже виртуальными функциями (это может измениться в будущем).источник
Вот определение
abstract class
в стандарте C ++n4687
13.4.2
источник
Результат: Площадь прямоугольника: 35 Площадь треугольника: 17
Мы видели, как абстрактный класс определял интерфейс в терминах getArea (), и два других класса реализовали одну и ту же функцию, но с другим алгоритмом для вычисления области, специфичной для фигуры.
источник