2014-01-23 4 views
8

Составляющие C++ компиляторы генерируют функции по умолчанию, такие как Constructor/Destructor/Copy-Constructor ... для этого «класса»?Создает ли C++ по умолчанию «Constructor/Destructor/Copy Constructor/Copy assign operator» для чистого виртуального класса?

class IMyInterface 
{ 
    virtual void MyInterfaceFunction() = 0; 
} 

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

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

Спасибо.

+4

'= 0;' разрешено выполнять только виртуальную функцию. – billz

+3

Это не C++. –

+0

@billz и гонки с легкостью на орбите: исправлено – user1911091

ответ

1

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

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

Если вы определяете по крайней мере одну виртуальную функцию в своем классе, вы также должны определить виртуальный деструктор.

Деструктор может быть определена с =default, хотя:

Вот исправленный пример (компилируется) код:

class ImyInterface 
{ 
    virtual void myInterfaceFunction() = 0; 
    virtual ~ImyInterface() = 0; 
} 

ImyInterface::~ImyInterface() = default; 
+2

Определение должно быть в файле '.cpp', иначе вы должны добавить ключевое слово' inline'. Кроме того, в чем смысл определения деструктора как чистого виртуального, когда другой член уже обозначил класс как абстрактный? – Potatoswatter

3

Да.

Нет формулировки, которая требует, чтобы класс был реалистичным, чтобы эти специальные функции-члены были объявлены неявно.

Это имеет смысл — только потому, что вы не можете создать экземпляр базы, не означает, что класс Derived не хочет использовать эти функции.

struct Base 
{ 
    virtual void foo() = 0; 
    int x; 
}; 

struct Derived : Base 
{ 
    Derived() {};   // needs access to Base's trivial implicit ctor 
    virtual void foo() {} 
}; 

См:

  • § 12,1/5 (т е р)
  • § 12.8/9 (перемещение)
  • § 12,8/20 (копия)
+3

«Не хочет использовать эти функции», конечно, очень мягкий способ его поместить - я не вижу никакого конструктора и деструктора, который мог бы даже избежать этого. В конце концов, конструктор базового класса вызывается до того, как тело конструктора производного класса будет когда-либо введено, и наоборот для деструкторов. –

+0

@ChristopherCreutzig: Действительно. –

1

Кроме того, я знаю, разумно ли определить вирту al destructor для чистого виртуального интерфейса, как и выше? (Таким образом, здесь нет указателей или данных, поэтому ничего не нужно разрушать)

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

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

struct A { 
    virtual ~A() {} 
    virtual int f() = 0; 
}; 

class B : public A { 
    std::ifstream fh; 
public: 
    virtual ~B() {} 
    virtual int f() { return 42; } 
}; 

std::shared_ptr<A> a = new B; 

Когда a выходит из области видимости, почему это ifstream закрыта? Поскольку деструктор удаляет объект с использованием виртуального деструктора.

0

Это относится ко второму вопросу об объявлении виртуального деструктора для абстрактного базового класса (например, по крайней мере одна функция-член является чистой виртуальной). Вот пример реального мира компилятора LLVM clang ++, улавливающего потенциальную проблему. Это произошло с версией инструментов командной строки, поставляемой Apple Developer для операционной системы Mac OS X Mavericks.

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

Ну, если абстрактный базовый класс не объявляет деструктор как виртуальный, компилятор clang ++ дает дружественное предупреждение о вызове не виртуального деструктора в абстрактном классе. Имейте в виду, что на самом деле только производные классы выделяются из кучи с помощью оператора new. Тип указателя производного класса из отношения наследования действительно представляет собой абстрактный тип базового класса (например, отношение is-a).

Если деструктор абстрактного базового класса не является виртуальным, то как будет вызываться дескриптор правильного производного класса для освобождения памяти? В лучшем случае компилятор знает лучше (по крайней мере, потенциально с C++ 11) и делает это возможным. Если опция -Wall компилятора включена, тогда должно появиться предупреждение о компиляции. Однако, в худшем случае, деструктор производного класса никогда не достигается, и память никогда не возвращается в кучу. Следовательно, в настоящее время происходит утечка памяти, которая может быть очень сложной для отслеживания и исправления. Все, что потребуется, - это однократное добавление «виртуального» к декларации деструктора абстрактного базового класса.

Пример кода:

class abstractBase 
{ 
    public: 
     abstractBase() { }; 
     ~abstractBase() { }; 

     virtual int foo() = 0; 
}; 


class derived : abstractBase 
{ 
    public: 
     derived() { }; 
     ~derived() { }; 

     int foo() override { return 42; } 
}; 

// 
// Later on, within a file like main.cpp . . . 
// (header file includes are assumed to be satisfied) 
// 
vector<abstractBase*> v; 

for (auto i = 0; i < 1000; i++) 
{ 
    v.push_back(new derived()); 
} 



// 
// do other stuff, logic, what not 
// 


// 
// heap is running low, release memory from vector v above 
//  
for (auto i = v.begin(); i < v.end(); i++) 
{ 
    delete (*i); // problem is right here, how to find the derived class' destructor? 
} 

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

class abstractBase 
{ 
    public: 
     abstractBase() { }; 
     virtual ~abstractBase() { }; // insert virtual right here 

     virtual int foo() = 0; 
} 

Обратите внимание, что абстрактный базовый класс имеет в настоящее время пустые конструкторы и тела деструктора. Как говорилось в Lightness, компилятор создает конструктор по умолчанию, деструктор и конструктор копирования для абстрактного базового класса (если он не определен инженером). Настоятельно рекомендуется просмотреть любые выпуски языка программирования C++ создателем C++, Bjarne Stroustrup, для получения более подробной информации об абстрактных базовых классах.

Смежные вопросы