C++函数的隐藏(遮蔽)、覆盖(多态)、重载

前沿

(1)函数重载发生在同一个类或顶层函数中,同名的函数而具有不同的参数列表
(2)函数覆盖发生在继承层次中,该函数在父类中必须是virtual,而子类的该函数必须与父类有相同的参数列表
(3)函数隐藏(遮蔽)发生在继承层次中,父类和子类同名的函数中,不属于函数覆盖的都属于函数隐藏
参考:https://www.cnblogs.com/xhyzdai/archive/2012/11/30/2796196.html

一、关于遮蔽,我们需要注意什么?

1、基类和派生类成员的名字一样时会造成遮蔽
2、不管函数的参数如何,只要名字一样就会造成遮蔽。换句话说,基类成员函数和派生类成员函数不会构成重载,如果派生类有同名函数,那么就会遮蔽基类中的所有同名函数,不管它们的参数是否一样。
3、即使派生类的成员(包括成员变量和成员函数)和基类中成员重名,造成遮蔽,仍然可以访问基类的成员变量和成员函数,不过要加上类名和域解析符。如:

Derived *p = new Derived;
p->Base::fun(); //使用指向派生类的指针p来调用基类Base中的函数fun;
cout << p->Base::i << endl; //输出基类中的变量i;

二、验证

2、1基类和派生类成员的名字一样时会造成遮蔽

#include<iostream>
using namespace std;
//基类People
class People{
public:
    void show();
protected:
    char *m_name;
    int m_age;
};
void People::show(){
    cout<<"嗨,大家好,我叫"<<m_name<<",今年"<<m_age<<"岁"<<endl;
}
//派生类Student
class Student: public People{
public:
    Student(char *name, int age, float score);
public:
    void show();  //遮蔽基类的show()
private:
    float m_score;
};
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}
void Student::show(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
int main(){
    Student stu("小明", 16, 90.5);
    //使用的是派生类新增的成员函数,而不是从基类继承的
    stu.show();
    //使用的是从基类继承来的成员函数
    stu.People::show(); 
    return 0;
}

关于第三点,即使派生类的成员(包括成员变量和成员函数)和基类中成员重名,造成遮蔽,仍然可以访问基类的成员变量和成员函数,不过要加上类名和域解析符。 如上面例子中的 stu.People::show();

2.2不管成员函数的参数如何,只要名字一样就会造成遮蔽

// 编译器:code::blocks gcc 4.9.2
#include<iostream>
using namespace std;
//基类Base
class Base{
public:
    void func();
    void func(int);
};
void Base::func(){ cout<<"Base::func()"<<endl; }
void Base::func(int a){ cout<<"Base::func(int)"<<endl; }
//派生类Derived
class Derived: public Base{
public:
    void func(char *);
    void func(bool);
};
void Derived::func(char *str){ cout<<"Derived::func(char *)"<<endl; }
void Derived::func(bool is){ cout<<"Derived::func(bool)"<<endl; }
int main(){
    Derived d;
    d.func("c.biancheng.net");
    d.func(true);
    d.func();  //error: no matching function for call to 'Derived::func()'|
    d.func(10);  //Derived::func(bool),这是因为实参“10”被隐式的转化成了bool类型;
    d.Base::func();
    d.Base::func(100);
    return 0;
}

三、多态(函数覆盖)

我们知道C++的虚函数是为了实现多态。对于虚函数,有以下的几点需要注意:

1、 只有派生类的虚函数遮蔽基类的虚函数(函数原型相同)才能构成多态(通过基类指针访问派生类函数)。

例如基类虚函数的原型为virtual void func1();,派生类虚函数的原型为virtual void func1(int i);,那么当基类指针 p 指向派生类对象时,语句p -> func1(100);将会出错,而语句p -> func1();将调用基类的函数。

这一点也比较好理解,基类指针p并不知道自己指向的是派生类的对象,才导致编译p->func(100)时,指针p自然会认为自己指向的是基类的对象,而基类对象中并没有这么一个函数原型,所以报错。例如:

#include<iostream>
using namespace std;
//基类Base
class Base{
public:
    virtual void func1();
};
void Base::func1(){ cout<<"Base::func1()"<<endl; }

class Derived: public Base{
public:
    virtual void func1(int);
    virtual void func2();
};
void Derived::func1(int i){cout << "Derived::func1(int i)" << endl; }
void Derived::func2(){ cout<< "Derived::func2()"<<endl; }

int main(){
    Base *b = new Derived;
    b->func1(); //多态
    //b->func1(100); //编译通不过,因为编译器只知道自己是个Base*,而Base中并没有func1(int)这个函数;
    //b->func2();    //编译通不过,因为编译器只知道自己是个Base*,而Base中并没有func2()这个函数;
    return 0;
}

2、构成多态时,仍然可以访问基类的成员变量和成员函数,不过要加上类名和域解析符

如:

#include<iostream>
using namespace std;
//基类Base
class Base{
public:
    virtual void func();
};
void Base::func(){ cout<<"Base::func()"<<endl; }

class Derived: public Base{
public:
    virtual void func();
};
void Derived::func(){cout << "Derived::func()" << endl; }

int main(){
    Base *b = new Derived;
    b->func(); //Derived::func()
    b->Base::func(); //Base::func()
    return 0;
}

关于通过虚函数实现多态的的实现方式,是通过一个虚函数表来实现的,具体见:C++ 虚函数表解析。即,虽然通过多态能够实现基类指针来调用派生类的函数,在派生类的对象模型中的虚函数表指针指向的虚函数表中也用派生类的func函数取代了基类的func函数(这是实现多态的根本原因),但是正如例子中看到的,通过指定类名,仍然可以访问基类的成员变量和成员函数。

猜你喜欢

转载自blog.csdn.net/baidu_35679960/article/details/80872478