C++ C++;继承相同类型签名的成员函数(阴影) //阴影 #包括 使用名称空间std; 常数int MNAME=30; 常数int M=13; 类Person{//基类 char person[MNAME+1]; 公众: 无效集(常量字符*n); 无效显示(ostream&)常数; 受保护的: 常量字符*name()常量; }; void Person::set(const char*n){ strncpy(person,n,MNAME); person[MNAME]='\0'; } void Person::显示(ostream&os)常量{ os

C++ C++;继承相同类型签名的成员函数(阴影) //阴影 #包括 使用名称空间std; 常数int MNAME=30; 常数int M=13; 类Person{//基类 char person[MNAME+1]; 公众: 无效集(常量字符*n); 无效显示(ostream&)常数; 受保护的: 常量字符*name()常量; }; void Person::set(const char*n){ strncpy(person,n,MNAME); person[MNAME]='\0'; } void Person::显示(ostream&os)常量{ os,c++,C++,您的Student类继承自Person。这意味着,Student对象由Student中定义的所有内部和Person中定义的所有内部组成。就此而言,Student可以被视为包含Person。这意味着Student对象包含display方法的两个版本-一个来自基类,另一个来自派生类。隐藏意味着从派生对象调用display时,它将调用派生类版本,并且基类版本为“隐藏”通过它而不是调用。您可以通过使用基类前缀显式指定它,从Student中调用隐藏版本:Person::display。通常,将被调用的函数

您的
Student
类继承自
Person
。这意味着,
Student
对象由
Student
中定义的所有内部和
Person
中定义的所有内部组成。就此而言,
Student
可以被视为包含
Person
。这意味着
Student
对象包含
display
方法的两个版本-一个来自基类,另一个来自派生类。隐藏意味着从派生对象调用
display
时,它将调用派生类版本,并且基类版本为“隐藏”通过它而不是调用。您可以通过使用基类前缀显式指定它,从
Student
中调用隐藏版本:
Person::display
。通常,将被调用的函数是作用域中最接近的函数-对于
派生的
对象,它是
派生的
和驻留在oute中的函数的作用域r范围(如底部)被遮蔽。

这意味着您很可能缺少一个
虚拟的

例如,您的Person类应该看起来像:

 // Shadowing

 #include <iostream>
 using namespace std;
 const int MNAME = 30;
 const int M  = 13;

 class Person {   // Base Class
     char person[MNAME+1];
   public:
     void set(const char* n);
     void display(ostream&) const;
   protected:
     const char* name() const;
 };

 void Person::set(const char* n) {
     strncpy(person, n, MNAME);
     person[MNAME] = '\0';
 }

 void Person::display(ostream& os) const {
     os << person << ' ';
 }

 const char* Person::name() const { return person; }

 class Student : public Person { // Derived
     int no;
     char grade[M+1];
   public:
     Student();
     Student(int, const char*);
     void display(ostream&) const;
 };

 Student::Student() {
     no = 0;
     grade[0] = '\0';
 }

 Student::Student(int n, const char* g) {
     // see p.61 for validation logic
     no = n;
     strcpy(grade, g);
 }

 void Student::display(ostream& os) const {
     os << name() << ' '
        << no << << ' ' << grade << endl;
 }

 int main() {
     Person person;
     Student student(975, "ABBAD");

     student.set("Harry");
     student.display(cout); // Harry 975 ABBAD

     person.set("Jane Doe");
     person.display(cout); // Jane Doe
 }
现在,如果您有以下代码:

 class Person {   // Base Class
     char person[MNAME+1];
   public:
     void set(const char* n);
     virtual void display(ostream&) const;
   protected:
     const char* name() const;
 };

您的输出将是“Harry”而不是“Harry 975 ABBAD\n”。正如icepack所说,您收到消息的原因是因为学生类“shadows”中的显示方法Person类中的display方法,由于您没有声明该方法为虚拟,编译器假定阴影是偶然的。如果不是偶然的,则应将该方法声明为虚拟的。

尝试此小实验。定义以下函数:

Person* student = new Student(975, "ABBAD")
student->set("Harry");
student->display(cout);
然后让
main
调用
person
student

void person_display(Person &p){
    p.display(cout);
}
您将看到,在这两种情况下,都将调用方法
Person::display
。阴影是一种现象,因为在类中对其祖先类的方法进行了重新定义。只要实例被视为子类,它就会阴影先前的定义,但一旦它被视为祖先,则阴影就是shado翅膀消失了


这与
virtual
方法形成对比,其中调用的方法始终是在真实实例类中定义的方法,即在上述实验中,您将看到调用
Student
方法,即使它被视为一个简单的
Person

想象您想在Student上调用Person版本。问题是关于阴影的。没有理由认为这是一个错误,也没有理由谈论重写。此外,编译器不会假设发生任何意外,阴影是一个有效的条件。如果不是错误(在我看来它肯定是一个错误),那么至少应该在Person代码中的行上有一条注释,解释
display
不应该是虚拟的,以及为什么(是的,我将编译器拟人化,阴影在语法上是正确的,但它是否有效取决于一个人对该词的使用。)
int main(){
   // [...]
   person_display(person);
   person_display(student);

}