C++的重写(覆盖)、重载、重定义(隐藏)、多态

“重写(覆盖)、重载、重定义、多态” 绝对称得上是C++里一个比较经典的问题了,下面我们来层层剖析它。


重写(覆盖)override

重写又名覆盖,常见于子类继承父类的时候,子类重写父类的某些方法。
有如下要求:

  • 父类中被重写的方法必须是virtual的
  • 子类中进行重写操作的那个方法,访问修饰符和父类的可以不同(即根据自身需要选择public、protected、private), 但是返回值、参数列表(参数的顺序、类型)和父类必须相同。

下面是一个重写的例子:

class B
{
private:
    virtual void f(int i, char c)
    {
        cout<<"BBB"<<endl;
    }
};

class A:B
{
public:
    void f(int j, char ch)
    {
        cout<<"AAA"<<endl;
    }
};

int main()
{
    A a;
    a.f(2,'a');
    return 0;
}

输出为:AAA


重载 overload

重载是指对同一个名称的函数,根据其参数的不同可以执行不同的操作。
举个栗子,下面的几种函数都属于重载,他们的要求就是参数表(参数个数、参数类型、参数顺序)必须至少要有一点不同!

inf f(){...}
int f(int a){...}
int f(char a){...}
int f(int a, char b){...}
....

注意啦,重载强调的是参数表要不同,而如果只改变返回值的类型的话则不属于重载,编译时会报错。比如如下的代码则不算是重载:

int f(){...}
void f(){...}

这个例子中编译会报:error: 'void A::f()' cannot be overloaded

重载还有一个地方需要注意,就是重载很容易产生二义性。
比如:

int f(int a){...}
int f(double a){...}

当你执行f(2)的时候,编译器不知道你要执行的是哪一个函数,这个时候的调用办法如下:

f((int) 2);			// 调用第一个函数
f((double) 2);	// 调用第二个函数

重载还可以用来重载运算符,这里就不作细致讨论了。


重定义(隐藏) redefining

重定义又称隐藏,它也常见于子类继承父类的情况中,注意区分重定义和重写的区别。
重定义是子类中有一个和父类方法名一样的方法,且父类中那个方法不能被virtual修饰,而且子类和父类中这个方法的参数表可以不同。

class A
{
public:
    void f()
    {
        cout<<"AAA"<<endl;
    }

};

class B:public A        // 这里必须是public或者protected,否则子类访问不了父类的方法
{
public:
    void f(int i)
    {
        cout<<i<<endl;
    }
};

int main()
{
    B b;
    // b.f(); 编译错误,父类方法已被隐藏
    b.A::f();      // 想访问父类方法,只能这样
    b.f(2);     // 这里是调用B类的方法
    return 0;
}


多态 polymorphism

多态是基于重写的,因此基类方法必须加virtual。
我们都知道父类指针可以指向子类对象,而多态就是用于给父类指针调用子类重写后的的方法的。
举个例子就明白了:

class A
{
public:
    void f()    // 非多态
    {
        cout<<"A-f()"<<endl;
    }
    virtual void g()    // 多态
    {
        cout<<"A-g()"<<endl;
    }


};

class B:public A
{
public:
    void f()
    {
        cout<<"B-f()"<<endl;
    }
    void g()
    {
        cout<<"B-g()"<<endl;
    }
};

int main()
{
    B b;
    A *a = &b;
    a->f();     // 没有多态,输出为:A-f()
    a->g();     // 有多态,输出为:B-g()

    return 0;
}

总结

  • 重写要求基类方法前加上virtual,且返回值及参数列表均需和基类相同。
  • 重载要求参数列表和原函数不同,仅有返回值不同不算重载,使用时需要注意二义性。
  • 重定义要求方法名和父类相同,其余可以不同,且此时要调用父类方法时只能采用::来调用
  • 多态是基于重写的一种程序调用方法,当基类的指针指向子类对象时可以调用子类的方法。
posted @ 2019-02-21 18:18  _吟游诗人  阅读(406)  评论(0编辑  收藏  举报