C ++编译器是否为这个“类”生成了Constructor / Destructor / Copy-Constructor等默认函数?
class IMyInterface
{
virtual void MyInterfaceFunction() = 0;
}
我的意思是不可能实例化这个“类”,所以我认为没有生成默认函数。 否则,人们说你必须使用虚拟析构函数。 这意味着如果我没有定义析构函数虚拟,它将是默认创建的,而不是虚拟的。
此外,我想知道为纯虚拟接口定义虚拟析构函数是否合理,如上所述? (所以这里没有使用指针或数据,因此不必破坏任何东西)
感谢。
答案 0 :(得分:3)
是强>
没有措辞要求类可以实例化,以便隐式声明这些特殊成员函数。
这是有道理的 - 只是因为你无法实例化Base,并不意味着Derived类不想使用这些函数。
struct Base
{
virtual void foo() = 0;
int x;
};
struct Derived : Base
{
Derived() {}; // needs access to Base's trivial implicit ctor
virtual void foo() {}
};
请参阅:
答案 1 :(得分:1)
此外,我想知道为纯虚拟接口定义虚拟析构函数是否合理,如上所述? (所以这里没有使用指针或数据,因此不必破坏任何东西)
这不仅是合理的,还是推荐的。这是因为在虚函数层次结构的情况下,(自动)调用专用类的析构函数也会调用它的基类的所有析构函数。如果未定义它们,则应该出现链接错误。
如果在类中定义至少一个虚函数,则还应定义虚拟析构函数。
析构函数可以使用=default
来定义:
这是一个更正(可编译)的代码示例:
class ImyInterface
{
virtual void myInterfaceFunction() = 0;
virtual ~ImyInterface() = 0;
}
ImyInterface::~ImyInterface() = default;
答案 2 :(得分:1)
此外,我想知道为纯虚拟接口定义虚拟析构函数是否合理,如上所述? (所以这里没有使用指针或数据,因此不必破坏任何东西)
衍生类是否会在析构函数中做任何事情?你能确定他们永远不会,即使其他人接管了开发吗?
拥有虚拟析构函数的关键不在于确保基类被正确破坏,无论如何都会发生。关键是当您使用通用接口时,将调用派生类的析构函数:
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
已关闭?因为析构函数使用虚析构函数删除对象。
答案 3 :(得分:0)
这解决了关于为抽象基类声明虚拟析构函数的第二个问题(例如,至少一个成员函数是纯虚拟的)。以下是LLVM clang ++编译器捕获潜在问题的真实示例。 Apple Developer为Mac OS X Mavericks操作系统提供的命令行工具版本就出现了这种情况。
假设您有一组派生类,这些派生类最终具有带抽象基类的父类来定义公共接口。然后有必要有一个像vector一样的存储容器,故意声明它存储指向每个元素的抽象基类的指针。稍后,按照良好的工程实践,需要“删除”容器元素并将内存返回到堆中。最简单的方法是逐个遍历vector元素并对每个元素调用delete操作。
好吧,如果抽象基类没有将析构函数声明为虚拟,那么clang ++编译器会提供关于在抽象类上调用非虚析构函数的友好警告。请记住,实际上只有派生类是使用operator 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 ++创建者Bjarne Stroustrup的任何C ++编程语言版本,以获取有关抽象基类的更多详细信息。