【C++虚函数】虚函数详解【下】

511 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

question

内联函数、构造函数、静态成员函数、模板函数可以是虚函数吗?

对于虚函数有几点关键点:

  • 虚函数是属于对象的
  • 虚函数的和运行时期有关

由以上两点,可以回答原问题。

  • inlineinline 需要在编译期就确定类的信息,但是虚函数具体是属于哪个类的,只有在动态运行时才能知道。
  • static:静态函数是没有 this 指针,而虚函数是属于某个对象的thisvptr来调用的。
  • constructor:虚函数等到运行时才知道是调用了哪个对象的虚函数。如果构造器也是虚函数,对象都无法构建。因此,构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还没有构造好, 多态是被 disable 的。
  • 模板函数 模板函数也不能是虚函数。因为,类会在vtbl中存放类中的所有的虚函数的函数指针,而一个模板函数如果设计为虚函数是无法获悉这个模板函数会被实例化为哪些具体的函数。

为什么需要虚析构函数,什么时候不需要? 父类的析构函数为什么要定义为虚函数

一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。这样做是为了当用一个基类的指针删除一个派生类的对象时, 派生类的析构函数会被正确调用。 当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。 所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

示例

     class Base { 
     public:
       virtual ~Base() { }
       virtual
       void show()  { std::cout<< "base_show"<<std::endl; }
       void print() { std::cout<< "base_print"<<std::endl; }
     };
 ​
     class Derived : public Base { 
     public:
       void print() { std::cout<< "Derived_print"<<std::endl; }
       void show()  { std::cout<< "Derived_show"<<std::endl; }
     };
 ​
     int main(int argc, char const *argv[])
     {
       Base* base = new Derived;
       base->print();  // base
       base->show();   // derived,是在运行期才会确定是哪个对象调用的 show
 ​
       delete base;
       return 0;
     }

上面示例中,print() 函数不是虚函数,因此 base->print(); 调用的是 Baseprint,这个函数调用在编译器就实现了。