1

我有一个基类 Student(),有两个派生类 GradStudent() 和 UndergradStudent()。

基类有一个名为 getInfo() 的函数,两个派生类有自己的 getInfo() 函数。

任务是创建一个学生数组并将每个班级的对象存储在该数组中。然后我从每个调用 getInfo() (对每个使用基类函数的版本),然后从两个派生类调用 getInfo() 函数。

对于我的生活,我无法弄清楚如何做到这一点。这是我到目前为止所拥有的:

(我想我应该在这里提一下,我不能修改基类)

    Student *classRoom[3];

    Student base ( "Jim", "Bean", 1111 );
    classRoom[0] = &base;

    GradStudent test ( "Jack", "Daniels", 1234, 'M', "Art" );
    classRoom[1] = &test;

    UndergradStudent ust1 ( "Johnny", "Walker", 1235, 1 );
    classRoom[2] = &ust1;

    cout << classRoom[1]->getInfo();

谁能指出我正确的方向?我会说说明它必须是一个数组,所以请不要使用矢量解决方案(无论如何仍然不熟悉它们)。

4

3 回答 3

2

如果您甚至无法修改派生类,则很难在不知道它们是特定类型的情况下对它们调用 GetInfo。也许您需要使用直接解决方案:

GradStudent gradStudent;
cout << gradStudent.GetInfo();

或者您可以尝试检查学生是否属于指定的派生类,但 IMO 它非常丑陋和危险(就扩展代码而言):

for (int i = 0; i < 3; i++)
{
    GradStudent * gs = dynamic_cast<GradStudent *>(classRoom[i]);
    if (gs != nullptr)
        cout << gs->GetInfo();
    else
    {
        UndergradStudent * ugs = dynamic_cast<UndergradStudent *>(classRoom[i]);
        if (ugs != nullptr)
            cout << ugs->GetInfo();
        else
            cout << classRoom[i]->GetInfo();
    }
}

我不得不说,多态性是专门为处理这种情况而创建的,而 GetInfo 在基类中不是虚拟的是严重的架构缺陷。

另外:如果您正在使用指针,请避免将地址检索到局部变量。那可能很危险。相反,手动分配和释放内存:

classRoom[0] = new Student;
classRoom[1] = new GradStudent;
classRoom[2] = new UndergradStudent;

// Process data

for (int i = 0; i < 3; i++)
    delete classRoom[i];

在边缘:我讨厌这样的任务:做 X 而不使用 Y(其中 Y 是专门为解决 X 而设计的)。


编辑:(回应评论)

你问,虚函数如何解决这个问题。假设一下,那Student::GetInfo是虚拟的。我们来分析以下几段代码:

Student * student = new Student;
student->GetInfo(); // Student::GetInfo is called

GradStudent * gradStudent = new GradStudent;
gradStudent->GetInfo(); // GradStudent::GetInfo is called

暂时没有什么大惊小怪的。

Student * student = new GradStudent;
student->GetInfo();
// if Student::GetInfo is virtual, GradStudent::GetInfo is called here
// if Student::GetInfo is not virtual, Student::GetInfo is called here

现在仔细阅读。IfStudent::GetInfo 是虚拟的并且在GradStudent类中实现,GradStudent::GetInfo将被调用,尽管事实上它是在Student变量上调用的。但是,如果Student::GetInfo 不是 virtual,在以前的情况下,Student::GetInfo将被调用。这基本上就是虚函数的工作方式。

在您的情况下,您有一组 Student * 变量 - 您不完全知道它们是Students、GradStudents 还是UndergradStudents。由于 yourStudent::GetInfo不是虚拟的,因此对这些变量调用此方法将始终导致调用Student::GetInfo,而不管它们的实际类型如何。

在这种情况下,我能想到的唯一解决方案是尝试猜测它们实际上是哪个类:

Student * student = new UndergradStudent;

Student * s = student; // Ok
GradStudent * gs = dynamic_cast<GradStudent *>(student); // Not true, will return NULL/nullptr
UndergradStudent * ugs = dynamic_cast<UndergradStudent *>(student); // Ok

这样就可以返回原来的类型,调用GetInfo实际的变量类型。

但请注意,这是一个丑陋的解决方案,这类问题是由虚函数解决的。

C++ FAQ是进一步阅读虚函数的好地方。

于 2013-02-14T10:37:27.210 回答
0

我对这个问题有点困惑,但这就是我从中得到的:

遍历 中的每个元素classRoom,调用getInfo()每个元素。然后输出 和 的test.getInfo()结果ust1.getInfo()

如果这是正确的,我认为老师可能试图指出的是,如果getInfo()正在调用的对象是派生类类型之一(GradStudentUndergradStudent),即使指向对象的指针是 type Student*getInfo()将始终调用派生类版本。

这是因为即使您调用getInfo()a Student*getInfo()也会在运行时绑定到派生类实现(因为getInfo()是虚拟的)。

于 2013-02-14T11:19:16.460 回答
0

如果getInfo()成员函数不是虚拟的,则分配的第一部分是迭代数组并调用getInfo()每个实例。

对于第二部分,您必须将派生类的实例向下转换为它们的真实类型,以便调用派生类的实际成员函数。

另外,我建议您按以下方式填充数组:

classRoom[0] = new Student( "Jim", "Bean", 1111 );
classRoom[1] = new GradStudent( "Jack", "Daniels", 1234, 'M', "Art" );
classRoom[2] = new UndergradStudent ( "Johnny", "Walker", 1235, 1 );

不要忘记在程序结束时删除它们。

于 2013-02-14T10:31:30.217 回答