Не помещает ли "виртуальный деструктор внутри интерфейса" сделать это, по определению, не интерфейсом?

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

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

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

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

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

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

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

virtual ~iFace();

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

virtual ~iFace() = 0;

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

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

virtual ~iFace() {};

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

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

Примечание. Все это объясняется вопросом: "Что такое интерфейс?" а затем, прочитав этот вопрос, ответьте: Как вы объявляете интерфейс в C++?

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

Ответ 1

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

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

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


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

НЕТ
C++ как язык не предоставляет Interface Вы называете Interface Абсолютный Abstract class в C++. Abstract Classes используются для моделирования поведения Interface в C++.

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


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

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


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

НЕТ
Для чистых виртуальных функций вполне справедливо определение.


Зачем мне нужна Pure virtual function с определением?
Код говорит громче, чем слова. Итак, вот простой пример:
Предупреждение: Uncompiled код только для демонстрации

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;
}

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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

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

Стандарт 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;
}

Ответ 4

  1. ОК.
  2. ОК; если функция-член не объявлена виртуальной в базовом классе, вызывается одна из базового класса; если функция-член не определена и не объявлена чистой виртуальной в базовом классе, вы получите сообщение об ошибке.
  3. В C++ у вас нет интерфейсов, как у вас в Java и С#; абстрактные базовые классы в C++ объединяют интерфейсы и абстрактные классы, поскольку они присутствуют на последних двух языках. Класс C++ является абстрактным, если он имеет хотя бы одну чистую виртуальную функцию-член.
  4. Замените интерфейс абстрактным классом.
  5. Формально вы не можете делать предположения о том, что произойдет, если вы удаляете производный класс из указателя на базовый класс, если деструктор базового класса не объявлен виртуальным.

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