虚函数
虚函数
-
虚函数的种类
-
non-virtual: 你不希望派生类重新定义
-
virtual:你希望派生类重新定义,且他有默认定义
-
pure virtual:你希望派生类一定要重新定义,你对他没有默认定义
1 2 3 4 5 6class Shape { public: virtual void draw() const = 0; // pure virtual virtual void error(const std::string &msg); // impure virtual int objectID() const; // non-virtual };
-
虚函数的实现原理
-
虚表与虚指针
-
我们先把下述代码扫一遍,并且来看看图,我们可以看到最左边的表 上面记录着 继承的父类和 自己的数据,但是除了这些之外,是不是还有一个记录着内存地址的,这个就是虚指针(vtpr)这个虚指针指向着一张虚表(vtbl)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25class A { pubilc: virtual void vfunc1(); virtual void vfunc2(); void func1(); void func2(); private: int m_data1, m_data2; }; class B : public A { pubilc: virtual void vfunc1(); void func2(); private: int m_data3; }; class C : public B { pubilc: virtual void vfunc1(); void func2(); private: int m_data1, m_data4; };
-
-
c++对一个虚函数进行调用的过程
- 他首先会考虑是静态绑定还是动态绑定
- 静态绑定 就是 call xxx call(汇编语言的一个动作) xxx(addr)
- 如果是满足三个条件就会动态绑定
- 通过指针来调用
- 指针满足向上转型
- 调用的是虚函数
- 只要满足这三个条件 就会变成
(*(p->vptr)[n])(p);或者(*p->vptr[n] )(p); - 为什么叫动态绑定,因为要看"p"是什么
虚函数的妙用
-
通过虚指针我们可以实现多态
-
比如 一个list 需要放多个不同大小的 比如 矩形 圆形 长方形 各种的 就可以直接放父类A 的指针 指向不同大小的 具体图形 因为对于指针来说 都是4个byte
1list<A*> myList;
-
虚函数的注意点
-
析构函数要写成虚函数
- 是为了降低内存泄漏的可能性
- 一个基类的指针指向一个派生类的对象,在使用完毕准备销毁时,如果基类的析构函数没有定义成虚函数,那 么编译器根据指 针类型就会认为当前对象的类型是基类,调用基类的析构函数,仅执行基类的析构,所以造成内存泄漏
-
构造函数不写成虚函数
- 我们创建一个对象,是需要知道对象的完整信息的。特别是要知道对象的具体类型,而虚函数只需要知道函数接口,而不需要知道具体类型,所以构造函数不应该被定义为虚函数
- 从目前编译器实现虚函数进行多态的方式来看,虚函数的调用是通过实例化之后对象 的虚函数表指针来找到虚函数的地址进行调用的,如果说构造函数是虚的,那么虚函数表 指针则是不存在的,无法找到对应的虚函数表来调用虚函数,那么这个调用实际上也是违 反了先实例化后调用的准则
-
构造函数和析构函数中不要调用虚函数
-
举例来说,有一个动物的基类,基类中定义了一个动物本身行为的虚函数 action_type(), 在基类的构造函数中调用了这个虚函数
派生类中重写了这个虚函数,我们期望着根据对象的真实类型不同,而调用各自实现的虚函 数,但实际上当我们创建一个派生类对象时,首先会创建派生类的基类部分,执行基类的构造 函数,此时,派生类的自身部分还没有被初始化,对于这种还没有初始化的东⻄,C++选择当 它们还不存在作为一种安全的方法
派生类中重写了这个虚函数,我们期望着根据对象的真实类型不同,而调用各自实现的虚函 数,但实际上当我们创建一个派生类对象时,首先会创建派生类的基类部分,执行基类的构造 函数,此时,派生类的自身部分还没有被初始化,对于这种还没有初始化的东⻄,C++选择当 它们还不存在作为一种安全的方法
-
在析构函数中也是同理,派生类执行了析构函数后,派生类的自身成员呈现未定义的状态,那 么在执行基类的析构函数中是不可能调用到派生类重写的方法的
-
-
哪些函数不能写成虚函数
- 构造函数: 上述已说明
- 内联函数: 内联函数表示在编译阶段进行函数体的替换操作,而虚函数意味着在运行期间进行 类型确定,所以内联函数不能是虚函数
- 静态函数: 静态函数不属于对象属于类,静态成员函数没有this指针,因此静态函数设置为虚 函数没有任何意义
- 友元函数: 友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数 的说法
- 普通函数: 普通函数不属于类的成员函数,不具有继承特性,因此普通函数没有虚函数
