避免对派生的非虚函数进行重定义
作者:网络转载 发布时间:[ 2013/1/30 9:55:39 ] 推荐标签:
无意中发现一个关于C++基础的问题,当时愣是没理解是什么原因,现在搞明白了,写下来了。先看小程序,先实践再理论吧,要不大家睡着了。
#include <iostream>
using namespace std;
class Base
{
public:
virtual void funtion(int arg = 1){cout<<arg<<endl;}
};
class Derive : public Base
{
public:
virtual void funtion(){cout<<"Derive"<<endl;}
virtual void funtion(int arg){cout<<"Derive"<<arg<<endl;}
};
int main(int argc, char *argv[])
{
Base* obj = new Derive();
obj->funtion();
system("pause");
return 0;
}
上面的程序会出现什么结果呢?我想会有很多人看到这个地方会怀疑我程序的正确性了,大呼“你的程序是错的”,但真的错吗?我们可以先执行下程序看下结果。很明显,结果是调用了子类的函数,并且子类中arg参数的值是父类中的值1,运行结果为“Derive 1”。
下面我解释下这种结果的原因,首先我先要说下对象的两种类型:动态类型和静态类型。
静态类型:指针或者是引用声明时的类型。
动态类型:由他实际指向的类型确定。
例如:
Base *pgo= //pgo静态类型是Base *
new Derive; //动态类型是Derive *
Asterioid *pa = new Asterioid; //pa的静态类型是 Asterioid *
//动态类型也是 Asterioid *
pgo = pa; //pgo静态类型总指向Base *
//动态类型指向了 Asterioid *
Base &rgo = *pa; //rgo的静态类型是Base
//动态类型是 Asterioid
虚函数是动态绑定的,而默认参数值是静态绑定的。运行时效率。如果默认参数值是动态绑定的话,那么编译器必须提供一整套方案,为运行时的虚函数参数确定恰当的默认值。而这样做,比起C++当前使用的编译时决定机制而言,将会更复杂、更慢。鱼和熊掌不可兼得,C++将设计的中心倾向了速度和简洁,你在享受效率的快感的同时,如果你忽略本条目的建议,你会陷入困惑。
其实对于这个问题在[Effective C++第3版]中也有提到,其第36条:避免对派生的非虚函数进行重定义。下面看下书中的描述:
现在考虑以下的层次结构:B是一个基类,D是由B的公有继承类,B类中定义了一个公有成员函数mf,由于这里mf的参数和返回值不是讨论的重点,因此假设mf是无参数无返回值的函数。也是说:
class B {
public:
void mf();
};
class D: public B { };
即使不知道B、D、mf的任何信息,让我们声明一个D的对象x:
D x; // x 是D类型的对象
B *pB = &x; // 指向x的指针
pB->mf(); // 通过指针调用mf函数
D *pD = &x; // 指向x的指针
pD->mf(); // 通过指针调用mf函数
在这里,如果告诉你pD->mf()与pB->mf()可能拥有不同的行为,你一定会感到意外。这也难怪:因为两次都是在调用x对象的成员函数mf,因为两种情况下都是用了同一函数和同一对象,mf()理所应当应该有一致的行为。难道不是吗?
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11