Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6319213
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类: C/C++

2014-09-11 12:23:35

原文地址:C++中名字查找与继承 作者:lvyilong316

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(域操作符::指定名字查找开始的位置).

 

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