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声明改变基类成员的访问权限