c++中的重载(Overload)、覆盖(重写,Override) 、隐藏与访问权限控制及using声明

这些概念有时记住了,但可能没多久就忘了,还是记下来吧。网上找的一篇不错:这里

 1 重载与覆盖

    成员函数被重载的特征:

(1)相同的范围(在同一个类中,不包括继承来的);

(2)函数名字相同;

(3)参数不同;(包括const和非const,这里const既指形参,也指函数本身)

//《C++ primer》中提到“仅当形参是引用或指针的时候,形参是否为const才对重载有影响。”
void f(int a);
void f(const int a);//error:重复定义

//引用或指针所指为const时ok。指针常量不可以。
void f(int *p);
void f(const int *p);//ok
void f(int *const p);//error:重复定义

//调用含const形参引用的函数时,若实参类型与形参不匹配(如非const变量),将尽可能做类型转换,并产生临时变量存储实参转换后的值。并把形参作为该对象的引用!!-->c++11转移构造和转移赋值函数可解 -- 右值引用

因此下面均为重载:
class A
{
public:
   void f(int *);
   void f(const int *);
   void f(int *) const;//因为隐含的this指针类型为const A*   
};

 

(4)virtual关键字可有可无。 

    覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual关键字。

令人迷惑的隐藏规则
本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

例:

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void vf(float x){ cout << "Base::f(float) " << x << endl; }

    void foo(){ cout << "Base::foo() " << endl; }
    virtual void g(float x){ cout << "Base::g(float) " << x << endl; }
    void h(float x){ cout << "Base::h(float) " << x << endl; }
}; 

 
class Derived : public Base
{
public:
    virtual void vf(float x){ cout << "Derived::f(float) " << x << endl; }
    
    void foo(int x){ cout << "Base::foo() " << x << endl; }
    void g(int x){ cout << "Derived::g(int) " << x << endl; } //隐藏-因为参数不同--即使基类函数是virtual
    void h(float x){ cout << "Derived::h(float) " << x << endl; }//隐藏-即使参数相同,但非virtual
};

void main(void)
{
    Derived d;
    Base *pb = &d;
    Derived *pd = &d;
    // 多态 --- 取决于指向的对象类型
    pb->vf(3.14f); // Derived::f(float) 3.14
    pd->vf(3.14f); // Derived::f(float) 3.14

    // 取决于指针类型
    pb->foo(); //Base::foo()
    //pd->foo(); //编译出错,派生类隐藏了Base::foo()
    pb->g(3.14f); // Base::g(float) 3.14
    pd->g(3.14f); // Derived::g(int) 3   -->隐式转换

    // 取决于指针类型
    pb->h(3.14f); // Base::h(float) 3.14
    pd->h(3.14f); // Derived::h(float) 3.14
}

上面的程序中:

(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。

 基类中被隐藏的函数可用作用域标识符直接调用,当然,在类外::只可访问public成员

pd->Base::foo();
pd->Base::g(3.14f);
pd->Base::h(3.14f);

当然,也可在类定义内部使用Base::foo();等等(派生类内(不管private继承还是protected继承)::可访问基类的public protected成员,但不能访问private函数)---这叫转发函数

也可以在派生类定义时使用using声明

class Derived : public Base
{
public:
  //让基类中所有名为 foo 和 g 的东西(如函数名 变量)在Derived的作用域中可见(并且是公有的)
  using Base::foo;
  using Base::g;    
  ...  
}

这样,之前对 pd->foo(); 等的调用都能成功

注意。不管如何继承,基类的私有成员都将在派生类中变的no access, using声明也无法使用基类的私有成员。关于继承级别和访问级别的权限控制,见这篇文章

using的作用域问题与继承时访问标号的关系,可见:派生类中用using声明改变基类成员的访问权限

posted @ 2016-01-21 16:26  sfqtsh  阅读(814)  评论(0编辑  收藏  举报