Разве «виртуальный деструктор внутри интерфейса» не делает его по определению больше не интерфейсом? - PullRequest
24 голосов
/ 06 августа 2011

Итак, вот коробка, в которой я нахожусь. Я хочу понять, почему важно иметь «виртуальный деструктор внутри вашего класса интерфейса». Вы поймете, почему этот материал в кавычках, если вы можете висеть до конца ... Я также хочу, чтобы весь словарь был абсолютно правильным. Вот где я сейчас нахожусь в процессе:

  1. Иногда у вас есть базовые классы, иногда у вас есть производные классы, которые наследуются от базовых классов.

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

  3. Интерфейс - это любой класс с только чисто виртуальными функциями. Если вы извлекаете новый класс из этого интерфейсного класса и реализуете все чисто виртуальные функции, то вы, наконец, можете создать экземпляр производного класса.

  4. У вас никогда не может быть экземпляра класса интерфейса, НО вы можете иметь экземпляр класса указателя на интерфейс.

  5. В случае, когда у вас есть указатель на интерфейсный класс, который на самом деле указывает на объект производного класса (на самом деле, я думаю, что это всегда будет необходимо, если # 4 будет правильным), и если вы решите удалить этот объект через ваш указатель, тогда, если у вас нет «виртуального деструктора внутри вашего класса интерфейса», ваше намерение уничтожить производный объект будет выполняться только как вызов для уничтожения базового объекта (то есть класса интерфейса ) и поскольку виртуального деструктора не существует, вещи никогда не дойдут до того, что деструктор для производного объекта будет фактически вызван, что приведет к утечкам памяти.

Уф. Хорошо, если это звучит правильно, на мой вопрос. Достаточно ли просто объявить виртуальный деструктор внутри вашего интерфейса следующим образом:

virtual ~iFace();

Это выглядит неправильно для меня ... так что произойдет, если вы сделаете деструктор чисто виртуальным, как это:

virtual ~iFace() = 0;

Поскольку они являются просто объявлениями, считается ли что-либо из них "виртуальным деструктором внутри вашего класса интерфейса"? Можете ли вы даже иметь объявленный, но неопределенный деструктор? Только если это чисто виртуально, я бы догадался ...

Так или иначе, так что возвращаясь к заглавному вопросу ... Я действительно иду так быстро, как могу ... Вот денежный выстрел ... Если ваш "виртуальный деструктор в вашем классе интерфейса" требует, по крайней мере, пустой определение как это:

virtual ~iFace() {};

Тогда эта функция-член не является чисто виртуальной (не может быть потому, что вы дали ей определение), и, следовательно, ваш класс больше не является интерфейсом (он не только содержит чисто виртуальные функции-члены).

Это будет означать, что если вы определите виртуальный деструктор для вашего интерфейса, то у вас больше не будет интерфейса (а только некоторый абстрактный базовый класс). Это просто злоупотребление языком? Я понимаю, что происходит?

примечание: все это произошло от вопроса к себе: «Что такое интерфейс? и затем прочитав ответы на этот вопрос: Как вы объявляете интерфейс в C ++?

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

Ответы [ 4 ]

26 голосов
/ 06 августа 2011

Почему деструктор Abstract class должен быть виртуальным и иметь определение?

Вызов delete для полиморфного указателя базового класса, указывающего на объект производного класса& Базовый класс, не имеющий виртуального деструктора, вызывает неопределенное поведение .

Итак, вам нужно объявить деструктор полиморфного базового класса как virtual.Как только вы объявите свой деструктор явно виртуальным, вам нужно будет дать определение для него.Это потому, что компилятор по умолчанию генерирует (определяет) деструктор для каждого класса, но если вы явно объявите деструктор, то компилятор не сделает этого и оставит вам возможность предоставить определение для вашего собственного деструктора.Это имеет смысл, потому что компилятор видит явное объявление как указание на то, что вы хотите выполнить некоторые нетривиальные операции (даже если вам это не нужно) в деструкторе, и это дает вам возможность сделать это, заставив вас датьопределение.


Миф 1:
В C ++ есть нечто, называемое Interface.

NO
C ++ как язык не обеспечивает Interface То, что вы называете Interface, называется Abstract class в C ++.Abstract Classes используются для имитации поведения Interface в C ++.

Что такое абстрактный класс?
По определению абстрактный класс должен иметь хотя бы одна чисто виртуальная функция.


Миф 2:
Все функции внутри класса Abstract должны быть чисто виртуальными.

NO
Abstract classes не требует, чтобы все функции внутри них были чисто виртуальными.Объект реферата не может быть создан, если он имеет хотя бы одну чисто виртуальную функцию.Однако, как вы правильно упомянули, вы можете создавать на него указатели.


Миф 3:
Чистые виртуальные функции не могут иметь определения.

NO
Для виртуальных функций Pure вполне допустимо иметь определение.


Почему быМне когда-нибудь нужен Pure virtual function с определением?
Код говорит громче, чем слова, поэтому вот простой пример:
Предупреждение: некомпилированный код только для демонстрации

class IMyInterface
{
    int i;
    int j;
    public:
        virtual void SetMembers(int ii, int jj)=0;
};

/*The pure virtual function cannot be inline in the class definition*/
/*So this has to be here*/
void IMyInterface::SetMembers(int ii, int jj)
{
    i = ii;
    j = jj;
}

class Myclass: public IMyInterface
{
    int k;
    int l;
    public:
        virtual void SetMembers(int ll, int m, int a, int b)
        {
             k = ll;
             l = m;
             IMyInterface::SetMembers(a,b);
         }
};

int main()
{

    MyClass obj;
    obj.SetMembers(10,20,30,40);
    return 0;
}
9 голосов
/ 06 августа 2011

C ++ не имеет нативной сущности интерфейса.Интерфейсы реализованы как обычные классы.

То, что делает класс интерфейсом в C ++, поэтому не является чем-то универсальным.Лично я считаю, что класс является интерфейсом, если у него нет членов данных, нет объявленных пользователем конструкторов, и все его функции являются чисто виртуальными - за возможным исключением его деструктора - и все его базовые классы, если таковые имеются, такжеинтерфейсы.Если класс не совсем соответствует всем этим свойствам, я мог бы назвать его «толстым» интерфейсом (как правило, не комплимент!).

Если вы хотите удалить динамически размещенные полиморфные классы через указатель набазовый класс (такой как класс «интерфейса»), тогда деструктор базового класса должен быть объявлен virtual.Это означает, что это должен быть объявленный пользователем деструктор, а не неявно объявленный деструктор, который будет не-virtual.

Как только вы объявите деструктор явным образом, вы должны предоставить для него реализацию.(Деструктор базового класса всегда будет использоваться, когда вы уничтожаете экземпляр любого производного от него класса, независимо от того, объявлен ли деструктор базового класса чисто виртуальным, виртуальным или не виртуальным.) Это чисто детали реализации языка C ++.Это не означает, что ваш базовый класс является чем-то вроде «интерфейса», если у вас есть интерфейсный класс, то очень вероятно, что реализация деструктора будет пустой в любом случае - у вас нет членов или базовых классов счлены, о которых нужно беспокоиться.

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

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

7 голосов
/ 06 августа 2011

"Интерфейс - это любой класс только с чисто виртуальными функциями"

- Концепция в C ++ называется abstract class . Абстрактный класс - это класс с не менее одной чистой виртуальной функцией. Однако не требуется, чтобы все его функции-члены были чисто виртуальными. Вы не можете создать экземпляр какого-либо абстрактного класса.

"Это будет означать, что если вы определите виртуальный деструктор для вашего интерфейс, то у вас больше нет интерфейса (но только некоторые абстрактный базовый класс). Это просто злоупотребление языком? Я понять, что происходит? "

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

Стандарт 12,4:

Деструктор может быть объявлен виртуальным (10.3) или чисто виртуальным (10.4); если в программе создаются какие-либо объекты этого класса или любого производного класса, деструктор должен быть определен.

Пример:

class A
{
public:
   // this is stil a pure virtual function
   // when there is a definition
   virtual ~A() = 0;
};

class B: public A
{};

int main()
{
   // fail to link due to missing definition of A::~A()
   B b;
}
2 голосов
/ 06 августа 2011
  1. OK.
  2. OK; если функция-член не объявлена ​​виртуальной в базовом классе, вызывается функция из базового класса; если функция-член не определена и не объявлена ​​чисто виртуальной в базовом классе, вы получите ошибку.
  3. В C ++ у вас нет интерфейсов, как у вас в Java и C #; абстрактные базовые классы в C ++ объединяют интерфейсы и абстрактные классы, поскольку они присутствуют в последних двух языках. Класс C ++ является абстрактным, если он имеет хотя бы одну чисто виртуальную функцию-член.
  4. Заменить интерфейс на абстрактный класс .
  5. Формально вы не можете делать предположения о том, что произойдет, если вы удалите производный класс из указателя на базовый класс, если деструктор базового класса не объявлен виртуальным.

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

...