C++的虚函数与作用域

2018-01-27 10:27:33来源:网络收集作者:纳米程序员人点击

分享

1.作用域的说明:


每个类定义自己的作用域,在这个作用域内我们定义类的成员。当存在继承关系时,派生类的作用域是嵌套在其基类的作用域内。如果一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义。


2.虚函数说明:


当我们使用基类的引用或指针调用一个虚成员函数时会执行动态绑定,因为我们直到运行时才能知道调用了哪个版本的虚函数,所以所有虚函数都必须有定义。通常情况下,如果我们不使用某个函数,则无需为该函数提供定义。但是我们必须为每一个虚函数都提供定义,而不管它是否被用到了,这是因为连编译器也无法确定到底会使用哪个虚函数。


3.继承中的名字查找


假定我们调用p->mem()(或者obj.mem()),则依次执行以下4个步骤:


<1>首先确定p(或obj)的静态类型,也就是属于哪个类。因为我们调用的是一个成员,所以该类型必然是类类型。


<2>在p(或obj)的静态类型对应的类中查找mem。如果找不到,则依次在直接基类中不断查找直至到达继承链的顶端。如果找遍了该类及其基类仍然找不到,则编译器将报错。


<3>一旦找到了mem,就进行常规的类型检查已确认对于当前找到的mem,本次调用是否合法。


<4>假设调用合法,则编译器将根据调用的是否是虚函数而产生不同的代码:


      如果mem是虚函数且我们是通过引用或指针进行的调用,则编译器产生的代码将在运行时确定到底运行该虚函数的哪个版本,依据是对象的动态类型。


      反之,如果mem不是虚函数或者我们是通过对象(而非引用或指针)进行的调用,则编译器将产生一个常规函数调用。


请看下面一个例子:class A
{
public:
void fun(int i)
{
cout << "A" << endl;
}
};
class B :public A
{
public:void fun()
{
cout << "B" << endl;
}};
int main()
{
B b;
b.fun();//正确。输出B
b.fun(2);//错误。“B::fun”: 函数不接受 1 个参数
system("pause");
return 0;
}按照一般的思路,类B会继承类A的void fun(int i)函数,那么就会与类B中自己定义的void fun()函数构成重载,但是事实上如果我们在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。


这也是说明了名字查找先于类型检查,编译器首先在类B中查找名字fun();此时类B确实定义了一个名为fun()的成员,所以查找终止。一旦找到了名字,编译器就不再查找了。类B中的fun()函数版本不需要实参,而当前的调用语句却提供了一个int型实参,所以第二个调用语句就是错误的。


所以基类与派生类中的虚函数必须有相同的形参列表。因为如果基类与派生类的虚函数接受的实参不同,那么就无法通过基类的引用或指针调用派生类的虚函数。


请看下面的例子:


class A
{
public:
virtual void fun(int i)
{
cout << "我在类A中定义" << endl;
}
};
class B :public A
{
public:
void fun(int j)
{
cout << "我在类B中定义" << endl;
}
};
class C :public A
{
public:
void fun()
{
cout << "我不是虚函数" << endl;
}
};
int main()
{
A a;
B b;
C c;
A *p1 = &a;
A *p2 = &b;
A *p3 = &c;
p1->fun(2); //虚调用,将在运行时调用A::fun
p2->fun(2); //虚调用,将在运行时调用B::fun
p3->fun(2); //虚调用,将在运行时调用A::fun
system("pause");
return 0;
}C++的虚函数与作用域


通过结果可以看出,类C中定义的fun函数就不属于虚函数,因为形参不同,所以只能被解析为类A定义的版本。


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台