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);
}