• 区分Overloading、Overriding及Hiding


    在面向对象(OO)的世界中存在着三个十分容易混淆的概念:重载(Overloading)、重写(Overriding)、隐藏(Hiding)。

    重载

    重载是指同一作用域的不同函数使用相同的函数名,但是函数的参数个数或类型不同。

    重载在C中就已经存在了,正如我们所熟悉的abs函数一样,如下所示:

    double abs(double); 
    int abs(int); 
    abs(1);         // call abs(int); 
    abs(1.0);       // call abs(double);
    

    重载函数就是在一个类空间里具有相同名字、不同参数的一些函数。比如下面类Maxer中的Max函数:

    class Maxer {
    public:
    
    	void Max(int a, int b);
    
    	void Max(double a, double b);
    
    	void Max(double a, double b, double c);
    
    	...// other code
    };
    

    但是,如果用派生类newMaxer继承基类Maxer:

    class newMaxer : public Maxer {
    public:
    
    	void Max(int a, double b);
    
    	...// other code
    };
    

    派生类newMaxer中的Max函数并不是基类Maxer中Max函数的重载兄弟,因为它们分属于不同的作用域。所以当写下如下代码时,编译器会报错:

    newMaxer newMax; 
    newMax.Max(1, 3);     // 编译报错
    

    这是因为在派生类的作用域中没有找到Max(int, int)的函数定义,基类Maxer中的Max被派生类中的Max(int, double)掩盖了,于是就出现了“参数不匹配”的错误提示。如果想让它们兄弟四个构成重载,需要将基类中的Max函数声明引入到派生类的作用域中,如下所示:

    class newMaxer : public Maxer {
    public:
    
    	using Maxer::Max;
    	void Max(int a, double b);
    
    	...// other code
    };
    

    重写

    重写是指在派生类中对基类中的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。重写是我们十分熟悉的一个操作,它与虚函数的实现息息相关。

    这里涉及两个关键要素:派生类和虚函数,如下所示:

    class Student {
    public:
    
    	Student(){}
    
    	~Student(){}
    
    	virtual void Show() {
    		std::cout<<"Student..."<<std::endl;
    	}
    	
    };
    
    class CollegeStudent : public Student {
    public:
    
    	CollegeStudent(){}
    
    	~CollegeStudent(){}
    
    	virtual void Show() {
    		std::cout<<"CollegeStudent..."<<std::endl;
    	}
    }
    

    但是重写有几点必须注意:

    • (1)函数的重写与访问层级(public、private、protected)无关。
    class CollegeStudent : public Student {
    public:
    
    	CollegeStudent(){}
    
    	~CollegeStudent(){}
    	
    private:
    
    	virtual void Show() {
    		std::cout<<"CollegeStudent..."<<std::endl;
    	}
    }
    

    上述派生类中的Show与基类的访问层级不同,但还是成功地实现了对该函数的特殊定制。

    • (2)const可能会使虚成员函数的重写失效。

    常量成员函数与一般的成员函数在函数签名中是不同的,其常量属性是函数签名中的一部分。

    class CollegeStudent : public Student {
    public:
    
    	CollegeStudent(){}
    
    	~CollegeStudent(){}
    	
    	virtual void Show()const {
    		std::cout<<"CollegeStudent..."<<std::endl;
    	}
    }
    

    因为具有不同的函数签名,所以派生类中的Show函数并没有重写基类中的Show函数。

    • (3)重写函数必须和原函数具有相同的返回类型。

    因为函数的返回类型不是函数签名的一部分,所以若派生类重写了基类类型中对应的函数,那么它们必须有相同的返回类型。如果返回值不同,编译器会抛出“重写虚函数返回类型有差异”的错误警示,如下所示:

    class CollegeStudent : public Student {
    public:
    
    	CollegeStudent(){}
    
    	~CollegeStudent(){}
    	
    	virtual bool Show() {
    		std::cout<<"CollegeStudent..."<<std::endl;
    	}
    }
    

    该规则存在一种例外情形,称为“协变返回值类型”。协变的返回值必须是子类或是父类的指针或是引用,如下所示:

    class CollegeStudent : public Student {
    public:
    
    	CollegeStudent(){}
    
    	~CollegeStudent(){}
    	
    	CollegeStudent& Show() {
    		std::cout<<"CollegeStudent..."<<std::endl;
    	}
    }
    

    需要注意的是,如果有返回值,返回值必须是子类或父类的引用或指针,如果父类的返回值是引用,那么子类返回值也是引用;如果父类返回值是指针,那么子类返回值也是指针。否则,编译将不通过。

    隐藏

    隐藏是指派生类中的函数屏蔽基类中具有相同名字的非虚函数。所以它的两个重要要素就是派生类和非虚函数。

    在调用一个类的成员函数时,编译器会沿着类的继承链逐级地向上查找函数的定义,如果找到就停止。如果一个派生类和一个基类有一个同名函数,由于派生类在继承链中处于下层,编译器则最终会选择派生类中的函数。如此一来,基类的同名成员函数就会屏蔽隐藏,编译器的函数查找也就到达不了基类中。

    还是采用前面的newMaxer类中的Max函数来说明这一问题,如下所示:

    class Maxer {
    public:
    
    	void Max(int a, int b);
    
    	void Max(double a, double b);
    
    	void Max(double a, double b, double c);
    
    	...// other code
    };
    
    class newMaxer : public Maxer {
    public:
    
    	bool Max(int a, int b);
    	void Max(int a, double b);
    
    	...// other code
    };
    

    当编译器在继承链中查找到Max函数时,派生类中的Max函数阻止了它向上寻找,隐藏了基类中的Max。

    总结

    最后,列出一张简单的表格让大家可以对这三者有个清晰的理解。

    关系 作用域 有无virtual 函数名 参数类型 返回值类型
    重载 相同 可有可无 相同 不同 可同可不同
    重写(覆盖) 不同 相同 相同 相同(协变)
    隐藏(重定义) 不同 可有可无 相同 可同可不同 可同可不同

    个人主页:

    www.codeapes.cn

  • 相关阅读:
    (转载)链表环中的入口点 编程之美 leecode 学习
    leecode single numer
    leecode 树的平衡判定 java
    Let the Balloon Rise
    Digital Roots
    大数加法,A+B
    小希的迷宫
    畅通工程
    lintcode596- Minimum Subtree- easy
    lintcode597- Subtree with Maximum Average- easy
  • 原文地址:https://www.cnblogs.com/codeapes666/p/12093774.html
Copyright © 2020-2023  润新知