C + + C++ C++:构造函数和析构函数能否为虚函数?
简单回答是:构造函数不能为虚函数,而析构函数可以且常常是虚函数。
1.构造函数不能为虚函数
让我们来看看大牛 C + + C++ C++之父 B j a r n e Bjarne Bjarne S t r o u s t r u p Stroustrup Stroustrup 在《 T h e The The C + + C++ C++ P r o g r a m m i n g Programming Programming L a n g u a g e Language Language》里是怎么说的:
T o To To c o n s t r u c t construct construct a n an an o b j e c t object object, a a a c o n s t r u c t o r constructor constructor n e e d s needs needs t h e the the e x a c t exact exact t y p e type type o f of of t h e the the o b j e c t object object i t it it i s is is t o to to c r e a t e create create. C o n s e q u e n t l y Consequently Consequently, a a a c o n s t r u c t o r constructor constructor c a n n o t cannot cannot b e be be v i r t u a l virtual virtual. F u r t h e r m o r e Furthermore Furthermore, a a a c o n s t r u c t o r constructor constructor i s is is n o t not not q u i t e quite quite a n an an o r d i n a r y ordinary ordinary f u n c t i o n function function, I n In In p a r t i c u l a r particular particular, i t it it i n t e r a c t s interacts interacts w i t h with with m e m o r y memory memory m a n a g e m e n t management management i n in in w a y s ways ways o r d i n a r y ordinary ordinary m e m b e r member member f u n c t i o n s functions functions d o n ′ t don't don′t. C o n s e q u e n t l y Consequently Consequently, y o u you you c a n n o t cannot cannot h a v e have have a a a p o n t e r ponter ponter t o to to a a a c o n s t r u c t o r . constructor. constructor.
— F r o m From From 《 T h e The The C + + C++ C++ P r o g a m m i n g Progamming Progamming L a n g u a g e Language Language》 15.6.2 15.6.2 15.6.2
翻译:普通函数要构造一个对象,构造函数需要它要创建的对象的确切类型。因此,构造函数不能是虚函数。此外,构造函数并不是一个很普通的函数,特别是它与内存管理的交互方式与普通成员函数不同,因此,你不能拥有一个构造函数的桥接器。
然而大牛就是大牛,这段话对一般人来说太难理解了。那下面就试着解释一下为什么:
这就要涉及到 C + + C++ C++对象的构造问题了, C + + C++ C++对象在三个地方构建:
- 函数堆栈
- 自由存储区,或称之为堆
- 静态存储区
无论在那里构建,其过程都是两步:首先,分配一块内存;其次,调用构造函数。好,问题来了,如果构造函数是虚函数,那么就需要通过 v t a b l e vtable vtable 来调用,但此时面对一块 r a w raw raw m e m e o r y memeory memeory(原始内存),到哪里去找 v t a b l e vtable vtable 呢?毕竟, v t a b l e vtable vtable 是在构造函数中才初始化的啊,而不是在其之前。因此构造函数不能为虚函数。
2.析构函数可以是虚函数,且常常如此
这个就好理解了,因为此时 v t a b l e vtable vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。
困惑我们的是我们却经常看到“虚构造函数”这样的说法,这就要归咎于不负责任或者说误人子弟的媒体了(包括书、技术文章等等)。因为他们说的是类似下面这样的做法:
class Expr {
public:
Expr();
Expr(const Expr&);
virtual Expr* new_expr() { return new Expr(); }
virtual Expr* clone() { return new Expr(*this); }
};