无法在C++中调用派生类函数

Who*_*ami 3 c++

我确定我错过了部分代码.

我有以下代码:

#include <iostream>

using namespace std;

class Record
{
private:
    int age;
    string name;
public:
    virtual int getType()=0;
};


class Student:  public Record
{
  private:
     int level_;
  public:
     Student()
     {
        level_=1;
     };
     ~Student() {};
     int getType()
     {
        return 1;
     }

     int level()
     {
        return level_;
     }
};


int main (int argc, char ** argv)
{
  Record *r = new Student();
  cout <<"tuype " << r->getType();
  cout <<"Class " << r->level();
}
Run Code Online (Sandbox Code Playgroud)

问题是:为什么我无法调用r->level()?调用它需要进行哪些更改?

Ton*_*roy 8

更改为记录以进行level()虚拟

你写了:

Record *r = new Student();
Run Code Online (Sandbox Code Playgroud)

在该行之后,编译器认为r是指向某个Record或某个Record派生类(它是)的指针,但它只知道为其指定的接口Record.没有virtual level()功能Record,因此您无法Student通过Record界面访问级别功能.只需添加这样的功能Record,你就可以了:

virtual int level() { return 0; }  // Student may override implementation
Run Code Online (Sandbox Code Playgroud)

要么

virtual int level() = 0;   // Student MUST override implementation
Run Code Online (Sandbox Code Playgroud)

另一种方法:检查Record*是否为学生提供解决方案

我上面说...

没有virtual level()功能Record,因此您无法Student通过Record界面访问级别功能.

...并显示如何添加到Record界面,但另一种方法是Student再次访问界面,如:

if (Student* p = dynamic_cast<Student*>(r))
    std::cout << "Level " << p->level() << '\n';
Run Code Online (Sandbox Code Playgroud)

第一行检查是否Record* r碰巧指向a Student(当然在你的代码中它总是如此,但想象你是在一个接受a的函数内部Record*,或者是在一些指针的容器上循环,其中一些是真的Students而其他的不是).如果是这样,返回的指针可用于将其作为Student对象访问,并且可以使用任何额外的功能/成员(如果某些Record功能以某种方式隐藏,则可能存在限制).

这种方法通常是不受欢迎的,因为它引出了一个问题:"为什么我们将这个问题Student视为一个Record我们需要知道的水平",而其他Record类型的类型甚至没有水平概念?不过,有时会发生类似的事情.添加一个virtual level函数Record是不理想的,如果它Student是唯一一个有一个有意义的值的派生类:这就是所谓的胖接口 - 你会在C++中找到它们的一些讨论.编程语言如果你有副本.

(sasha.sochka的回答是第一个提到dynamic_cast选项 - 请upvote)

基类应该有虚拟析构函数

根据chris的评论,你应该添加到Record:

virtual ~Record() { }
Run Code Online (Sandbox Code Playgroud)

这可确保在派生对象delete使用基类指针时调用派生类的析构函数实现(例如,如果delete r;在底部添加a main()).(编译器仍将确保之后调用基类析构函数).

这是未定义的行为,如果你不这样做,并且充其量你会发现在派生类中添加的任何其他数据成员都没有调用它们的数据成员......对于int那些无害的,但是因为std::string它可能会泄漏内存,或者甚至持有一个锁,以便程序稍后挂起.当然,依靠最佳案例的未定义​​行为并不是一个好主意;-)但我认为除非你制作基础析构函数,否则理解什么是绝对不会有用virtual.

推荐学生的小改进

如果你level virtual进入Record,那么为了使Student类的读者更清楚,它level()virtual一个基类的函数实现,你可以使用override关键字,如果你有一个适当的C++ 11兼容的编译器:

int level() override
{
    return level_;
}
Run Code Online (Sandbox Code Playgroud)

如果它无法在基类中找到匹配(非const)virtual int level(),这将给您一个编译器错误,因此它可以避免一些偶尔的故障排除.virtual如果您认为它具有文档值,您也可以重复该关键字(对于C++ 03而言,override这不是一个选项特别好),但它没有任何功能差异 - 该函数保持不变virtual(隐式或显式) )从基类重写虚函数.