C++:重载、覆盖和隐藏

重载、覆盖和隐藏

共同点:函数名称相同。

(1)重载:必须在一个域内,函数名称相同但是函数参数不同。重载的作用就是同一个函数有不同的行为。

重载完全是一个编译时(或静态)的概念。如果声明了同名函数,编译器会在编译时处理这些同名函数的调用问题,

确定调用哪一个函数,运行时不 涉及调用重载函数的额外开销或决定。而且,重载只用于函数,不用于类。

函数重载的特征:(1)相同的范围(在同一个类中);(2)函数名称相同;(3)参数不同;(4)virtual关键字可有可无

分析:因为函数参数不同,可以简单的理解为:两个重载函数是不同的函数,调用者能够明确的根据不同的参数来调用不同的函数。

那么如果存在这样两个函数,编译器怎么处理呢?

例子:

class A
                     {
                      public:
                      void Func(int a, int b=0) {printf("This is Func1/n");}
                      void Func(int a) {printf("This is Func2/n");}
                      };

int main()
                     {
                         A a;
                         a.Func(5);
                         return 0;
                      }

当然,对于这样两个函数,调用者不知道应该调用哪个函数,故编译器直接报错。

(2)覆盖:也称为重新定义(redefinition)。派生类希望覆盖所继承的行为,使得派生类的对象表现出自身的行为而非基类对象的行为。

覆盖的特性:(1)不同的范围(函数分别位于派生类与基类);(2)函数名字相同;(3)参数相同;(4)基类函数必须有virtual

关键字。

我们发现,这里用到了虚函数,实际上虚函数的作用,就是实现覆盖。

分析:既然和虚函数挂钩,说明了这个是多态支持的特性(通过基类指针或引用间接指向派生类子类型时才有效)。

只有在基类中声明为virtual的成员函数才能在派生类中被覆盖,不可能覆盖在基类中未声明为virtual的函数。但是,仅在基类

的某函数前加virtual关键之,派生类不能据此将其解释为允许覆盖该函数,基类的设计者应该将可覆盖的函数声明为虚函数,

并在文档中明确陈述派生类可覆盖该函数。总之一句话,覆盖函数都是虚函数,反之,不然。

覆盖是运行时的概念,它在运行时从一组被覆盖的函数中选择一个函数。只有动态绑定才能使用覆盖机制。通过覆盖,使

用的是对象所实现的正确的方法,而不是调用接口所使用的方法。覆盖基于单个函数,派生类的方法覆盖在基类中声明的同名

方法,覆盖只应用于类中的方法,不能应用于自由函数。覆盖意味着继承关系,没有继承,就不存在覆盖。

例子:

class Base

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

class Derived : public Base

{
                             public:
                             virtual void g(void){ cout << "Derived::g(void)" << endl;}
                             };

void main(void)
                            {
                              Derived d;
                              Base *pb = &d;
                              pb->f(42);      // Base::f(int) 42

pb->f(3.14f);   // Base::f(float) 3.14
                              pb->g();      // Derived::g(void),即多态
                             }

函数Base::f(int)与Base::f(float)相互重载,而Base::g(void)被Derived::g(void)覆盖。

(3)隐藏:指的是派生类的成员函数隐藏了基类函数的成员函数。隐藏的意思是,虽然现在看不到,但是并不表明不存在,只是暂时被隐

藏起来了而已,所以,如果将一个子类的对象赋值给一个基类的指针,那么用该指针调用对应的成员函数时,调用的是被派生

类隐藏了的对应的基类的成员函数。

隐藏的规则:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。

(注意别与重载混淆)

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。

(注意别与覆盖混淆)

分析:隐藏一词可以这么理解:在调用一个类的成员函数的时候,编译器会沿着类的继承链逐级的向上查找函数的定义,如果

找到了那么就停止查找了,所以如果一个派生类和一个基类都有同一个同名(暂且不论参数是否相同)的函数,而编译器

最终选择了在派生类中的函数,那么我们就说这个派生类的成员函数"隐藏"了基类的成员函数,也就是说它阻止了编译

器继续向上查找函数的定义。

例子(1):

class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << 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 f(float x){ cout << "Derived::f(float) " << x << endl; }
virtual void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};

在这个例子(1)中,(a)函数Derived::f(float)覆盖了Base::f(float)。
(b)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
(c)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。

总结:重载与隐藏:相同点:(1)函数名相同(2)参数都不同(3)virtual关键字可有可无。

重载的函数,virtual关键字可有可无。

不同点:(1)范围不同。重载的函数在同一个范围内(同一个类),隐藏的函数在基类和派生类。

覆盖与隐藏:相同点:(1)范围相同:函数在基类和派生类(2)函数名相同(3)参数相同

不同点:(1)virtual关键字。基类如果没有virtual关键字,则被隐藏。而覆盖,基类必须有virtual关键字。

即,覆盖的函数是多态的,是存在于vtbl之中的函数才能构成“覆盖”关系,而隐藏的函数

都是一般的函数,不支持多态,在编译阶段就已经确定下来了。

例子(2):

void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;

// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14

// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14 (surprise!)
pd->g(3.14f); // Derived::g(int) 3

// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}

在例子(2)中,令人非常不解,bp和dp指向同一地址,按理说运行结果应该是相同的,可事实并非这样。所以我们还是要

想办法摆脱隐藏。

分析:出现surprise!的地方,还是和函数的动、静态绑定有关系。f函数是虚函数,但是h函数不是。声明为虚函数的类里面,

都包含一个vptr指针和一个所谓的虚函数表(vtbl)。在程序编译的时候,函数指针(指向函数的指针变量)f、g、h就已

经是确定的了,但是vptr指针根据不同的偏移量而不同,且这个变化是在运行期动态决定的。也就是说,函数的地址不

可变,但是vptr可变。代码Base *pb = &d;只是用Derived类对象d的vptr修改了Base类pb的vptr指针,但是当Base

类成员建立的时候,f,g,h函数指针是不能改变的。

当函数声明为virtual的时候,就激活了多态机制,程序在运行的时候会根据类型的实际类型到vtbl中查找函数指针,

去调用正确的函数来执行。h函数指针的类型在编译的时候已经确认了,所以在程序运行的时候不能发生改变,从而发生

了在子类中的“隐藏”现象。

注意:还有一点要切忌,对于static这种静态成员函数,是属于类的方法,而不是对象的方法,

所以static方法绝对不能被覆盖或者隐藏。

时间: 2024-10-02 10:17:06

C++:重载、覆盖和隐藏的相关文章

C++ 类函数的重载,覆盖和隐藏

覆盖(override): 覆盖指的是派生类的虚拟函数覆盖了基类的同名且参数相同的函数!函数覆盖覆盖,要满足两个条件 (1)基类中函数声明有virtual关键字 (2)基类函数和派生类的函数要函数名,参数,返回类型三者皆一致. 重载(overload):必须在一个域中,函数名称相同但是函数参数不同,重载的作用就是同一个函数有不同的行为,因此不是在一个域中的函数是无法构成重载的,这个是重载的重要特征 隐藏(hide): 派生类的成员函数隐藏了基类函数的成员函数.隐藏一词可以 这么理解:在调用一个类

C++之重载覆盖和隐藏

继承体系下同名成员函数的三种关系 重载 在同一作用域内 函数名相同,参数列表不同(分三种情况:参数个数不同,参数类型不同,参数个数和类型都不同) 返回值类型可以相同也可以不同 重写(覆盖) 在不同作用域内,分别在父类和子类 函数名相同,参数列表相同,返回值类型相同,协变除外(下面会介绍什么是协变) 基类函数必须有virtual关键字修饰 父子类中函数的访问修饰符可以不同 重定义(隐藏) 在不同的作用域内,分别是父类和子类 函数名相同 在基类和派生类中只要不构成重写的都是重定义 重载 C++中函数

C++重载,覆盖和隐藏区别

a.成员函数被重载的特征:(1)相同的范围(在同一个类中):(2)函数名字相同:(3)参数不同:(4)virtual 关键字可有可无. 因为函数参数不同,可以简单的理解为:两个重载函数是不同的函数,调用者能够明确 的根据不同的参数来调用不同的函数.那么如果存在这样两个函数,编译器怎么处理呢? class A{public:    void Func(int a, int b=0) {printf("This is Func1/n");}    void Func(int a) {pri

c++重载、覆盖和隐藏

c++成员函数的重载.覆盖和隐藏很容易混淆,因此我们必须搞清楚这一点. 1.重载 重载特点:同一访问区域(同类).函数名称相同.函数参数不同(不关心返回类型).virtual关键字可有可无. 代码示例: class OverLoad { public: void test() {} void test(int i) {} void test(float i) {} void test(int i, float j) {} }; 2.覆盖 覆盖是指派生类函数覆盖基类函数,特点是:不同访问区域(不同

C++ 成员函数的重载与覆盖与隐藏

重载与覆盖 成员函数被重载的特征: (1)相同的范围(在同一个类中): (2)函数名字相同: (3)参数不同: (4)virtual 关键字可有可无. 覆盖是指派生类函数覆盖基类函数,特征是: (1)不同的范围(分别位于派生类与基类): (2)函数名字相同: (3)参数相同: (4)基类函数必须有 virtual 关键字. 函数 Base::f(int)与 Base::f(float)相互重载,而 Base::g(void) 被 Derived::g(void)覆盖. #include <ios

C++函数的重载,覆盖和隐藏(——高质量编程)

函数重载概念 只有C++才有重载的概念,C语言没有. 靠行参列表的不同来区别不同的重载函数, 若是全局函数和成员函数同名时,不算重载,因为它们的作用域不同,所以成员函数会将全局函数给隐藏(遮蔽)了. 成员函数的重载,覆盖和隐藏 重载发生在相同的作用域中,而覆盖则发生在不同的作用域中 重载: 相同的作用域 函数的名字相同 函数的形参列表,顺序,类型,个数不同 virtual可有可无 覆盖: 作用域不同,属于不同的类中 函数的名字相同 形参列表相同 基类必须是虚函数

c/c++:重载 覆盖 隐藏 overload override overwrite

http://www.cnblogs.com/qlee/archive/2011/07/04/2097055.html 成员函数的重载.覆盖与隐藏成员函数的重载.覆盖(override)与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防.8.2.1 重载与覆盖成员函数被重载的特征:(1)相同的范围(在同一个类中):(2)函数名字相同:(3)参数不同:(4)virtual 关键字可有可无.覆盖是指派生类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派生类与基类):(2)函数名

C++:类成员函数的重载、覆盖和隐藏区别?

#include <iostream> class A { public: void func() { std::cout << "Hello" << std::endl; } void func(int k) { } }; class B : public A { public: using A::func; // 把这句注释掉试试,嘿嘿 void func(int i) { } }; int main() { B b; b.func();//编译

C++重载、覆盖、隐藏的区别与举例

参考博客:http://blog.csdn.net/hexi_2000/article/details/4392107 //重载,覆盖,隐藏举例 #include <iostream> using namespace std; class A { public:     int n;     void fun()     {         cout<<"A::fun()"<<endl;     }     virtual void fun(int 

【非原创】C++类成员函数的重载、覆盖和隐藏

链接:https://www.nowcoder.com/questionTerminal/266d3a6d4f1b436aabf1eff3156fed95来源:牛客网 题目:类成员函数的重载.覆盖和隐藏区别描述正确的有? A.覆盖是指在同一个类中名字相同,参数不同 B.重载是指派生类函数覆盖基类函数,函数相同,参数相同,基类函数必须有virtual关键字 C.派生类函数与基类函数相同,但是参数不同,会"隐藏"父类函数 D.函数名字相同,参数相同,基类无virtual关键字的派生类的函数