• c++ 继承: 虚函数,同名隐藏,覆盖


     不同作用域声明的标识符的可见性原则:

        如果存在两个或多个具有包含关系的作用域,外层声明了一个标识符,而内层没有再次声明同名标识符,那么外层标识符在内层依然可见,如果在内层声明了同名标识符,则外层标识符在内层不可见,这时称内层标识符隐藏了外层同名标识符,这种现象称为隐藏规则。

        在类的派生层次结构中,基类的成员和派生类新增的成员都具有类作用域。二者的作用范围不同,是相互包含的两个层,派生类在内层。这时,如果派生类声明了一个和某个基类成员同名的新成员,派生的新成员就隐藏了外层同名成员,直接使用成员名只能访问到派生类的成员。如果派生类中声明了与基类同名的新函数,即使函数的参数表不同,从基类继承的同名函数的所有重载形式也都被隐藏。如果要访问被隐藏的成员,就需要使用类作用域分辨符和基类名来限定。

        作用域分辨符,就是"::",它可以用来限定要访问的成员所在的类的名称。一般的使用形式是:

        类名::成员名

        类名::成员名(参数表)

        关于同名隐藏的验证:

    代码1:

    1. #include<iostream>  
    2. using namespace std;  
    3. class A  
    4. {  
    5.     public:  
    6.   
    7.   
    8.     void print2(){  
    9.         cout<<"A print2 !"<<endl;  
    10.     }  
    11. };  
    12.   
    13.   
    14. class B:public A  
    15. {  
    16.       
    17.     public:  
    18.   
    19.   
    20.     void print2(int x){  
    21.         cout<<"B print2 !"<<x<<endl;  
    22.     }  
    23. };  
    24.   
    25.   
    26. int main(){  
    27.     B b;  
    28.     b.print2();  
    29.     return 0;  
    30. }  
    #include<iostream>
    using namespace std;
    class A
    {
    	public:
    
    
    	void print2(){
    		cout<<"A print2 !"<<endl;
    	}
    };
    
    
    class B:public A
    {
    	
    	public:
    
    
    	void print2(int x){
    		cout<<"B print2 !"<<x<<endl;
    	}
    };
    
    
    int main(){
    	B b;
    	b.print2();
    	return 0;
    }

        由结果可知已经不能直接从B的对象中直接用函数名访问print2()了。

        将b.print2();  改为: b.A::print2();

        可见能利用域分辨符来定位继承自A被隐藏的print2()函数。

        除了利用域分辨符定位继承自基类并被派生类隐藏的成员以外,还可以利用using关键字加以说明。

        using的一般功能是将一个作用域中的名字引入到另一个作用域中,它还有一个非常有用的用法:将using用于基类中的函数名,这样派生类中如果定义同名但参数不同的函数,基类的函数将不会被隐藏,两个重载的函数将会并存在派生类的作用域中。

    代码2:

    1. #include<iostream>  
    2. using namespace std;  
    3. class A  
    4. {  
    5.     public:  
    6.   
    7.     void print2(){  
    8.         cout<<"A print2 !"<<endl;  
    9.     }  
    10. };  
    11.   
    12. class B:public A  
    13. {  
    14.       
    15.     public:  
    16.     using A::print2;  
    17.     void print2(int x){  
    18.         cout<<"B print2 !"<<x<<endl;  
    19.     }  
    20. };  
    21.   
    22. int main(){  
    23.     B b;  
    24.     b.print2();  
    25.     return 0;  
    26. }  
    #include<iostream>
    using namespace std;
    class A
    {
    	public:
    
    	void print2(){
    		cout<<"A print2 !"<<endl;
    	}
    };
    
    class B:public A
    {
    	
    	public:
    	using A::print2;
    	void print2(int x){
    		cout<<"B print2 !"<<x<<endl;
    	}
    };
    
    int main(){
    	B b;
    	b.print2();
    	return 0;
    }

    编译运行:

    虚函数与运行时多态:

    代码3:

    1. #include<iostream>  
    2. using namespace std;  
    3. class A  
    4. {  
    5.     public:  
    6.   
    7.     void print(){  
    8.         cout<<"A print !"<<endl;  
    9.     }  
    10. };  
    11.   
    12. class B:public A  
    13. {  
    14.       
    15.     public:  
    16.     void print(){  
    17.         cout<<"B print !"<<endl;  
    18.     }  
    19. };  
    20.   
    21. int main(){  
    22.     A *a=new B();  
    23.     a->print();  
    24.     return 0;  
    25. }  
    #include<iostream>
    using namespace std;
    class A
    {
    	public:
    
    	void print(){
    		cout<<"A print !"<<endl;
    	}
    };
    
    class B:public A
    {
    	
    	public:
    	void print(){
    		cout<<"B print !"<<endl;
    	}
    };
    
    int main(){
    	A *a=new B();
    	a->print();
    	return 0;
    }

        类型兼容规则:在需要基类的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。

        类型兼容规则中所指的替代包括以下的情况:

        派生类的对象可以隐含转换为基类对象。

        派生类的对象可以初始化基类的引用。

        派生类的指针可以隐含转换为基类的指针。

        在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。

        代码3中的new B()返回一个指向B对象的指针,之后隐式转换为指向A的指针a,因为a是指向A类对象的指针,因此可以用a来访问继承自A的类B的对象,但是a只能使用继承自A的成员。

        根据赋值兼容规则,可以使用派生类的对象替代基类对象。如果用基类类型的指针指向派生类对象,就可以通过这个指针来访问对象,问题是访问到的只是从基类继承来的同名成员。解决这一问题的方法是:如果需要通过基类的指针指向派生类的对象,并访问某个与基类同名的成员,那么首先在基类中将这个同名函数说明为虚函数。这样,通过基类类型的指针,就可以使属于不同派生类的不同对象产生不同的行为,从而实现运行过程的多态。

        将代码3稍作修改:

        代码4:

    1. #include<iostream>  
    2. using namespace std;  
    3. class A  
    4. {  
    5.     public:  
    6.   
    7.     virtual void print(){  
    8.         cout<<"A print !"<<endl;  
    9.     }  
    10. };  
    11.   
    12. class B:public A  
    13. {  
    14.       
    15.     public:  
    16.     void print(){  
    17.         cout<<"B print !"<<endl;  
    18.     }  
    19. };  
    20.   
    21. int main(){  
    22.     A *a=new B();  
    23.     a->print();  
    24.     return 0;  
    25. }  
    #include<iostream>
    using namespace std;
    class A
    {
    	public:
    
    	virtual void print(){
    		cout<<"A print !"<<endl;
    	}
    };
    
    class B:public A
    {
    	
    	public:
    	void print(){
    		cout<<"B print !"<<endl;
    	}
    };
    
    int main(){
    	A *a=new B();
    	a->print();
    	return 0;
    }

        

  • 相关阅读:
    Python文件操作
    两个标签页定位第二个标签页元素时显示element not visible
    Selenium Webdriver元素定位的八种常用方式
    Python集合set
    Python 字典
    数据库存储过程
    打印1-100
    》》》 《类的多态》
    》》》《类的继承》
    》》》关于eclipse中git同步代码报错checkout conflict with files的解决方法
  • 原文地址:https://www.cnblogs.com/czsl/p/3109292.html
Copyright © 2020-2023  润新知