Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3426723
  • 博文数量: 198
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7246
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(198)

文章存档

2023年(9)

2022年(4)

2021年(12)

2020年(8)

2019年(18)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: C/C++

2014-08-31 16:13:13

C++中名字查找与继承

——lvyilong316

C++调用成员函数的步骤一般为:

(1)     首先确定函数调用的对象、引用或指针的静态类型

(2)     在该类中查找函数,如果找不到就在直接基类中找,如此循环直到查找到最上层基类。

(3)     一旦找到了改名字就进行常规的参数类型检查。

(4)     假定函数调用是合法的,编译器就生成代码。如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定对象的动态类型运行哪个函数版本。

注意:名字查找发生在编译期(即使是虚函数调用),而且与参数检查是不同阶段。

如:

class Base

{

public:

        void fun(){}

};

class Derived:public Base

{

public:

       void fun(int a){}

};

Derived d;

d.fun();//调用出错,因为编译器首先找到的是Derived中的fun,所以不在查找,但是参数检查时发现类型不匹配。

对于通过基类指针或引用调用虚函数,同样编译器要在基类中查找函数名称,假定找到了函数名称,编译器就检查实参与形参是否匹配。这就是为什么虚函数必须在基类和派生类中拥有同一原型了。

名字冲突造成的覆盖问题

原则:如果基类和派生类使用相同名字的成员函数,在派生类作用域中派生类成员将屏蔽基类成员,即使函数原型不同,基类成员也会被屏蔽。

如:

class Base

{

public:

        virtual void fun()

        {

               cout<<"Base::fun()"<

        }

};

class Derived1:public Base

{

public:

       void fun(int a) //虽然不是虚函数,但也将Base中的虚函数fun()隐藏了

       {

              cout<<"Derived1::fun(int)"<

       }

//这里有继承自Base的虚函数fun

};

class Derived2:public Derived1

{

public:

       void fun(int a) //隐藏了Derived1fun(int)

       {

              cout<<"Derived2::fun(int)"<

       }

       void fun()  //重写了Base的虚函数

       {

          cout<<"Derived2::fun()"<

       }

};

       Base b;

       Derived1 d1;

       Derived2 d2;

       Base *pb1=&b,*pb2=&d1,*pb3=&d2;

       pb1->fun();   //Base::fun()

       pb2->fun();   //Base::fun()

       pb3->fun();   //Derived2::fun()

下面分析两个经典面试题。

l  1

class

protected

       int m_data; 

public

       A(int data = 0) 

       { 

              m_data = data; 

       } 

       int GetData() 

       { 

              return doGetData(); 

       } 

       virtual int doGetData() 

       { 

              return m_data; 

       } 

}; 

class B: public

protected : int m_data; 

public:  

       B(int data = 1) 

       { 

              m_data  =  data; 

       } 

       int doGetData()   

       { 

              return m_data; 

       } 

}; 

class C : public

protectedint m_data; 

public

       C(int data = 2) 

       { 

              m_data = data; 

       } 

}; 

int _tmain(int argc, _TCHAR* argv[])

{

       C c(10); 

       cout<//1

       cout<//1

       cout<//1

       cout<//1

       cout<//1

       cout<//0

       cout<//1

       cout<//1

       return 0; 

}

分析:

c.GetData(),首先在C的作用域中查找GetData,没有找到,然后再B中查找,依然没有,最后在A中查找,找到了这个函数。之后参数检查没发生错误则生成调用代码。注意在GetData(成员函数)中调用了doGetData(虚函数)。其实GetData的原型应该是:

       int GetData(A* this

       { 

              return this->doGetData(); 

       }

这里通过指针调用虚函数,会发生动态绑定。因为this指向的c(派生类对象),所以要调用C中的虚函数doGetData。那C中有没有这个函数呢?虽然直接没有,但是别忘了C中有继承自B中的虚函数表,也就是调用B中的这个doGetData

之后的c.B::GetData()c.C::GetData()c.A::GetData()其实都一样,因为BC中都没有GetData,所以都是调用的A中的GetData

下面c.doGetData(),由于是通过对象c调用虚函数doGetData,所以不会发生动态绑定。和调用普通函数一样,首先在当前作用域查找doGetData,没有,然后向上在B中找到了,就会调用B中的doGetData(输出1). 至于c.B::doGetData()c.C::doGetData()作用相同,因为在C中没哟doGetData()

l  2

class

protected

       int m_data; 

public

       A(int data = 0) 

       { 

              m_data = data; 

       } 

       int GetData() 

       { 

              return doGetData(); 

       } 

       int doGetData() 

       { 

              return m_data; 

       } 

}; 

class B: public

protected : int m_data; 

public:  

       B(int data = 1) 

       { 

              m_data  =  data; 

       } 

       int doGetData() 

       { 

              return m_data; 

       } 

}; 

class C : public

protectedint m_data; 

public

       C(int data = 2) 

       { 

              m_data = data; 

       } 

}; 

int _tmain(int argc, _TCHAR* argv[])

{

       C c(10); 

       cout<//0

       cout<//0

       cout<//0

       cout<//0

       cout<//1

       cout<//0

       cout<//1

       cout<//1

       return 0; 

}

分析:

c.GetData(),编译器首先在C中找GetData,没有找到就像上层基类B中寻找,也没有找到,再向A中寻找,发现A中有GetData()则调用。所以c.GetData()是调用A中的GetData。而GetData中又调用doGetData,而doGetData由于不是虚函数,所以也不会发生动态绑定,调用GetDatathis是指向A的指针,所以就会调用A中的doGetData(A中开始查找doGetData的名字)

A::GetData()B::GetData()C::GetData()的作用相同,只是查找的起始范围不同(分别从ABC中开始),但由于BC中没有GetData,最终都是调用的A中的。

c.doGetData(),会先在C中查找doGetData,没有找到,向上一级B中查找,发现有这个函数就调用,所以调用的B中的doGetDatac.A::doGetData()c.B::doGetData()分别指定从AB中开始查找,因为AB中都有doGetData,所以分别调用AB中的doGetData(域操作符::指定名字查找开始的位置).

 

阅读(5956) | 评论(2) | 转发(4) |
给主人留下些什么吧!~~

lvyilong3162014-09-03 19:31:52

zhiye_wang:您的原话:A::GetData()、B::GetData()、C::GetData()的作用相同,只是查找的起始范围不同(分别从A、B、C中开始),但由于B、C中没有GetData,最终都是调用的A中的.
既然B是继承于A,那么就应该继承了A的GetData函数,为什么说B和C中没有GetData?

这个你应该这么理解:B继承A,也就继承了A的函数这没错,但是函数的作用域不变。举个例子:你继承了你邻居家的物品,他们的物品属于你了,但是还是在你邻居家,并不在即房间里,虽然名义上是你的,你也可以使用,但是物品的位置没有变。这个时候假如你需要一件物品,比如电视机,你会现在自己家找,没有找到再去邻居家找。所以我说B中没有GetData(),指的是你自己家没有电视机,但并不是你没有电视机。不知道这样举例你明白了没

回复 | 举报

zhiye_wang2014-09-03 09:16:35

您的原话:A::GetData()、B::GetData()、C::GetData()的作用相同,只是查找的起始范围不同(分别从A、B、C中开始),但由于B、C中没有GetData,最终都是调用的A中的.
既然B是继承于A,那么就应该继承了A的GetData函数,为什么说B和C中没有GetData?