我搜索了这个,但我真的不明白答案。
我对C ++是全新的,我想要实现的是拥有一个抽象类,它作为我的对象类型的基类,以便我可以将我的对象存储在抽象类型的指针数组中而不是使用void *
。此外,我的对象共享一些常见的成员函数,这些函数可以通过抽象类实现轻松减少我的代码库。
但是,我对抽象类的构造函数和析构函数感到困惑。
抽象类实际上并不需要构造函数,因为可以传递的参数对于两者都是通用的,需要在派生类中使用所述参数执行不同的操作以正确设置受保护的属性(矩阵的大小) )。那么,没有构造函数可以吗?另外,由于我没有构造函数,析构函数应该是什么?
我说的是实现虚拟析构函数的答案。但是,我不确定这意味着什么,并且讨论了潜在的内存泄漏,只要派生类重新实现了析构函数,就不会有任何内容泄漏。所以,这确实意味着我可以实现一个虚拟的decstructor,然后在派生的对象中说Foo
和Bar
我只是实现~Foo
和~Bar
来防止内存泄漏(假设他们当然是对的)?我不相信我理解派生类中的重新实现意味着什么。
答案 0 :(得分:8)
通常,在实现抽象基类时,您有两个推荐的析构函数选项(source):
当您打算拥有基类的指针时,请使用此选项,这可能指向派生类的实例。例如:
class MyBase {
public:
virtual ~MyBase() {};
};
class MyDerived : public MyBase {
public:
virtual ~MyDerived() {};
}
std::unique_ptr<MyBase> pInstance = std::make_unique<MyDerived>();
通过在基类(以及派生类)中使析构函数为虚拟,您可以确保在运行时调用MyDerived
的析构函数。 如果析构函数是非虚拟的,则在指向delete
的指针上调用MyBase
将不会调用MyDerived
的析构函数。
如果您不希望允许用户创建指向派生对象的基类指针,请使用此选项。
class MyBase {
protected:
~MyBase() {};
};
class MyDerived : public MyBase {
public:
~MyDerived() {};
}
// NOT ALLOWED: std::unique_ptr tries to call protected destructor.
std::unique_ptr<MyBase> pBadInstance = std::make_unique<MyDerived>();
// Allowed: std::unique_ptr calls public MyDerived::~MyDerived()
std::unique_ptr<MyDerived> pGoodInstance = std::make_unique<MyDerived>();
然而,这有一个重要的警告。如果您有一个深度继承层次结构,那么拥有一个非虚拟析构函数意味着您必须在层次结构中强制执行此规则。例如:
class MyBase {
protected:
~MyBase() {};
};
class MyDerived : public MyBase {
public:
~MyDerived() {};
}
class MyDerivedAgain : public MyDerived {
public:
~MyDerivedAgain() {};
}
// Uh oh! MyDerivedAgain destructor would not be called!
std::unique_ptr<MyDerived> pGoodInstance = std::make_unique<MyDerivedAgain>();
如果选择这条路线,则应确保不允许实例化任何基类。 除叶子派生类外的所有析构函数都应受到保护。
这看起来有点复杂,但它可以具有诸如避免vtable空间等优点,并且通过在运行时避开vtable查找来轻微改善紧密循环中的性能(最好是微优化)。
完全可以省略任何类中的构造函数,只要所有变量都可以默认构造(或者没有构造函数(例如int))。 C ++编译器将简单地创建默认构造函数MyAbstractClass::MyAbstractClass() { }
。也就是说,通常最好创建一个构造函数来初始化任何抽象类变量:
<强>好:强>
class MyBase {
protected:
int _x;
int _y;
};
class MyDerived : public MyBase {
public:
MyDerived(int x, int y) {
_x = x;
_y = y;
}
};
<强>更好的:强>
class MyBase {
public:
MyBase(int x, int y) : _x(x), _y(y) {
}
protected:
int _x;
int _y;
};
class MyDerived : public MyBase {
public:
MyDerived(int x, int y) : MyBase(x, y) {
}
};
&#34;更好&#34;使用MyBase::MyBase(int, int)
构造函数的版本更好,因为它会强制_x
和_y
立即初始化,编译器将检查基类构造函数是否被调用。通过在派生构造函数中初始化基类变量,您可能会引发灾难,因为您可能忘记来初始化变量并导致各种运行时问题。
如果要实现定义&#34;合同&#34;的接口类,则可以跳过构造函数(接口不具有变量或实现,不需要构造函数),并使用公共虚拟析构函数。这可以确保实现接口的任何类在删除时都会被清除。
class MyInterface {
public:
virtual ~MyInterface() = 0;
virtual void MyMethod() = 0;
virtual void MyOtherMethod() = 0;
};
// Base class virtual destructors should always have an implementation,
// even when they are pure-virtual.
MyInterface::~MyInterface() { }
// -----------------------------------------------------------------------------
class MyImplementation : public MyInterface {
virtual ~MyImplementation () { }
virtual void MyMethod() { std::cout << "MyMethod()" << std::endl; }
virtual void MyOtherMethod() { std::cout << "MyOtherMethod()" << std::endl; }
};
答案 1 :(得分:1)
一般来说,c ++中的抽象类应该提供virtual
析构函数定义。这也可以是抽象的
virtual ~MyClass() = 0;
// Define a body in any case
MyClass::~MyClass() {}
抽象类不需要声明构造函数(不能是纯virtual
函数BTW)。
当抽象类不是普通接口(没有数据成员)时,构造函数可能有意义。