C++ 基类方法重载、重写、隐藏
成员函数重载
- 重载构成的条件是:在同一作用域下的函数,函数名相同,参数列表不同构成重载。
class Base { public: Base(int data) :ma(data) { cout << "Base(int data)" << endl; } Base() = default; ~Base() { cout << "~Base()" << endl; } void show() { cout << "Base::show()" << endl; } void show(int val) { cout << "Base::show(int)" << endl; } protected: int ma; }; class Deriver :public Base { public: Deriver(int data) :mb(data), Base(data) { cout << "Deriver(int data)" << endl; } ~Deriver() { cout << "~Deriver()" << endl; } void show(int a, int b, int c) { cout << "Deriver::show(int,int,int)" << endl; } private: int mb; };
- 重载的原理:C++编译代码时生成的符号名由函数名+参数列表组成,所以只要生成的符号不一样,那么就可以重载,不会产生编译问题,与返回值无关
- 重载成员函数是对于类内而言的:
A 中的三个 func 函数是重载 overload。class A{ public: void func(); void func(int); // overload void func(double, double); // overload }
重写
- 重写构成的条件:大前提,一个方法 A 在基类中是虚函数;子类中存在一个与父类的那个虚函数返回值、参数列表相同的函数 B ,则父类虚函数 A 被子类继承后被 B 函数覆盖;子类方法 B 此时也是一个虚函数。
class Base { public: Base(int data) :ma(data) { cout << "Base(int data)" << endl; mc = data; } ~Base() { cout << "~Base()" << endl; } virtual void show() { cout << "Base::show()" << endl; } //在子类中被重写的show方法 void show(int val) { cout << "Base::show(int)" << endl; } //被子类隐藏的show方法 int mc; protected: int ma; }; class Deriver :public Base { public: Deriver(int data) :mb(data), Base(data) { cout << "Deriver(int data)" << endl; } ~Deriver() { cout << "~Deriver()" << endl; } virtual void show(){ cout << "Deriver::show()" << endl; } //也是一个虚函数 private: int mb; };
- 基类的同名虚方法在派生类中被重新定义,称为重写 override:
注意:虚函数重写时,派生类虚函数的返回类型要与基类一致,否则报 error: conflicting return type specified for virtual…,但是如果基类虚函数返回类型是基类指针或引用,派生类虚函数可以返回派生类指针或引用,这被称为返回类型协变:class A{ public: virtual void func(); } class B : public A{ public: virtual void func(); // override }
class A{ public: virtual A func(); virtual A& func(); } class B : public A{ public: virtual B func(); // error! virtual B& func(); // ok }
隐藏
- 隐藏的构成条件:派生类中含有与基类同名的方法,编译器会优先选择使用派生类中同名方法,看上去派生类将基类的同名方法隐藏。只要函数名相同即可构成隐藏,与函数返回值和参数列表无关,如果一定要调用基类的方法,可以在方法前加类作用域,明确的告诉编译器我们要使用基类的方法,否则编译器默认使用派生类方法。
class Base { public: Base(int data) :ma(data) { cout << "Base(int data)" << endl; } Base() = default; ~Base() { cout << "~Base()" << endl; } void show() { cout << "Base::show()" << endl; } void show(int val) { cout << "Base::show(int)" << endl; } protected: int ma; }; class Deriver :public Base { public: Deriver(int data) :mb(data), Base(data) { cout << "Deriver(int data)" << endl; } ~Deriver() { cout << "~Deriver()" << endl; } void show(int a, int b, int c) { cout << "Deriver::show(int,int,int)" << endl; } int show(bool flg) { cout << "Deriver::show(bool)" << endl; return 0; } private: int mb; }; int main() { Deriver d(100); //Deriver::show d.show(true); d.show(1,2,3); //Base::show d.Base::show(); d.Base::show(99); return 0; }
- 方法隐藏也是对于基类和派生类的同名方法而言的,包含两种情况:
- 基类方法是非虚方法的,派生类方法同名,基类成员函数被派生类成员函数隐藏:
被隐藏的基类方法必须通过类作用域解析 A:: 才能被派生类对象调用:class A{ public: void func1(); } class B : public A{ public: void func1(int); // hide A::func1() virtual void func1(); // hide A::func1() }
B b; b.func1(); // B::func1() b.A::func1(); // A::func1() b.func1(1) // B::func1(int)
- 基类和派生类方法都是虚函数,但同名不同参,基类被派生类方法隐藏:
如果直接通过对象调用类方法:class A{ public: virtual void func1(); } class B : public A{ public: virtual void func1(int); // hide A::func1() }
由于基类虚函数被隐藏,派生类对象 b 只能看到 B::func1(int) 方法,因此报 error: no matching function for call to function…B b; b.func1(); // error! b.func1(1); // B::func1(int) b.A::func1(); // A::func1()
如果通过基类指针或引用调用类方法:
由于基类虚函数没有被重写而是被隐藏,基类指针或引用调用的类方法仍然是基类方法。B b; A* pa = &b; pa->func1(); // ok pa->func1(1); // error!
如果基类与派生类出现同名数据成员,基类成员数据也会被隐藏,需要通过类名解析指定使用。总而言之,如果派生类需要使用被隐藏的基类成员,则必须通过 基类名:: 使得被隐藏的成员可见。
- 基类方法是非虚方法的,派生类方法同名,基类成员函数被派生类成员函数隐藏:
虚函数的重写与隐藏
- 只有当基类与派生类的虚方法同名同参时,才能被重写。如果基函数的虚方法被隐藏,显然无法实现多态性。 因此,派生类重写基类虚函数应当遵循以下要求:
- 派生类虚函数原型与基类虚函数完全相同,包括返回类型。(除返回类型协变外)
- 如果基类虚函数被重载,则派生类虚函数也应当被重载。(如果只重写一个虚函数,则其它被重载的基类虚函数将被隐藏)
- 重载函数重写示例:
class A{ public: virtual void func(); virtual void func(int); // overload virtual void func(double, double); // overload } class B : public A{ public: virtual void func(); // override virtual void func(int); // override virtual void func(double, double) // override }