zoukankan      html  css  js  c++  java
  • [C++]虚函数-同名访问

          首先来看一下派生类和基类成员同名事的处理规则:

    1. 派生类内定义了一个与基类同名的成员,该现象称为同名覆盖,此时,无论派生类内部成员函数还是派生类的对象访问同名成员,如果未加任何特殊标识,则访问派生类中重新定义的同名成员。
    2. 如果派生类内部成员或派生类的对象需要访问基类继承来的同名函数,则必须在同名函数前加上"基类名::"进行类名限定。
    3. 如果基类内部成员函数或基类对象访问同名成员,访问的一定是基类的同名成员。
    4. 用基类的指针,无论是否指向基类对象,都只能访问基类的同名成员。
    5. 用基类的引用,无论是否是基类对象的别名,都只能访问基类的同名成员。

          从4、5两条规则可以看到,无论基类指针指向基类对象还是派生类对象,无论引用是基类对象还是派生类对象的别名,始终调用基类成员。那么,怎样才能使基类指针指向基类对象时调用基类同名成员,指向派生类对象时调用派生类同名成员呢?这就是动态联编所能达到的效果。

          为了实现动态联编,首先要将同名函数声明为虚函数

          虚函数可以通过基类指针或引用访问基类和派生类中被声明为虚函数的同名函数。存在继承关系是首要条件,而且派生类一定是以公有方式继承了基类。同名虚函数在基类和派生类中其函数原型必须完全一致,否则无法通过虚函数实现动态多态性。

          第一:为什么派生类必须以公有方式继承基类呢?这是赋值兼容规则的使用前提,以下规则反向均不成立。

    1. 基类对象=公有派生类对象。赋值后的基类对象只能获得基类成员部分,而在派生类中新增加的成员不能被基类对象访问。
    2. 指向基类对象的指针=公有派生类对象的地址。利用赋值后的指针可以间接访问基类的成员,若要间接访问派生类成员,必须强制转换为派生类类型指针访问。
    3. 指向基类对象的指针=指向公有派生类对象的指针。
    4. 基类的引用=公有派生类对象,即派生类对象可以初始化基类的引用。赋值后的引用只可以访问基类成员,无法访问派生类新增成员,因为无法对引用进行强制类型转换

          一般来说,可以将类层次中具有共性的成员函数声明为虚函数,而个性的函数没有必要声明为虚函数。但以下情况是特例:

    1. 静态成员函数不能声明为虚函数。因为静态成员函数不属于某一个对象,没有多态性。
    2. 内联函数不恩能够声明为虚函数。因为内联函数的执行代码是明确的,没有多态性的特征。
    3. 构造函数不能是虚函数。因为构造函数声明为虚函数是完全没有意义的。
    4. 析构函数往往被声明为虚函数。

           如果基类的析构函数声明为虚函数,则该类所有派生类的析构函数也自动成为虚函数而无需显式声明。在这种情况下,由于实施多态性是将基类的指针指向派生类的对象,若果删除该指针,则会调用该指针指向的派生类的析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象被完全释放。如果析构函数不是虚函数,则编译器实施静态绑定,在删除基类指针时,只调用指针所属基类的析构函数而不调用派生类的析构函数,这会导致析构不完全。所以当派生类中存在指针数据成员,又通过该指针进行了动态空间申请时,将析构函数声明为虚函数是非常有必要的。

            第二:为什么同名虚函数在基类和派生类中其函数原型必须完全一致?一个函数在基类和公有派生类中拥有相同的函数名,但函数返回值类型不同,或者是形式参数表不同,即使在基类中被声明为虚函数,也不具备多态性。这种情况下,基类中的函数无虚函数特性,当做普通函数使用,而在派生类中存在同名函数就是本文开始讲到的同名覆盖现象,无法通过基类的指针或引用实现动态多态性。

         下面的几条面试题可以巩固本文的知识。

    1.题目是问下面的C++代码输出是什么?(阿里巴巴)

    class Base
    {
    public:
    	int Bar(char x) { return (int)x;}
    	virtual int Bar(int x) { return (2*x);}
    };
    
    class Derived : public Base
    {
    public:
    	int Bar(char x) { return (int)(-x);}
    	int Bar(int x) { return (x/2);}
    };
    
    void main()
    {
    	Derived Obj;
    	Base* pObj=&Obj;
    	printf("%d,",pObj->Bar((char)(100)));
    	printf("%d,",pObj->Bar(100));
    }
    

      答案是:100,50

     2. What's the output of the following code?(微软)

    class A
    {
    public:
    	virtual void f()
    	{
    		cout << "A::f()" << endl;
    
    	}
    	void f() const
    	{
    		cout << "A::f() const" << endl;
    	}
    };
    
    class B : public A
    {
    public:
    	void f()
    	{
    		cout << "B::f()" << endl;
    	}
    	void f() const
    	{
    		cout << "B::f() const" << endl;
    	}
    };
    void g(const A * a)
    {
    	a->f();
    }
    
    int main()
    {
    	A * a = new B();
    	a->f();
    	g(a);
    	delete a;
    }
    

      答案是:B::f()
                      A::f() const

    转载请注明出处:http://www.cnblogs.com/Rosanna/p/3346046.html 

  • 相关阅读:
    java:线上问题排查常用手段
    去fastjson笔记
    如何让java中的注释代码执行?
    spring中aop不生效的几种解决办法
    mysql事务隔离级别/脏读/不可重复读/幻读详解
    业务系统-全球化多时区的解决思路
    mysql虚拟列(Generated Columns)及JSON字段类型的使用
    freeswitch笔记(7)-放音控制
    JVM问题典型案例定位学习
    freeswitch笔记(6)-会议功能简介
  • 原文地址:https://www.cnblogs.com/Rosanna/p/3346046.html
Copyright © 2011-2022 走看看