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

标签 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();
}

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

最佳答案

对记录的更改 level()虚拟

你写了:

Record *r = new Student();

在该行之后,编译器会考虑 r是指向 Record 的指针或一些 Record -派生类(它是),但它只知道为 Record 指定的接口(interface).没有virtual level()函数在 Record ,因此您无法访问 Student的关卡功能通过Record界面。只需在 Record 中添加这样的功能即可你会没事的:
virtual int level() { return 0; }  // Student may override implementation

或者
virtual int level() = 0;   // Student MUST override implementation

另一种选择:检查记录*是否指向学生

我上面说...

There is no virtual level() function in Record, so you can not access Student's level function through the Record interface.



...并展示如何添加到 Record界面,但另一种方法是访问 Student再次界面,如:
if (Student* p = dynamic_cast<Student*>(r))
    std::cout << "Level " << p->level() << '\n';

第一行检查是否 Record* r恰好指向一个 Student (当然,在您的代码中它总是如此,但想象一下您在一个接受 Record* 的函数中,或者正在循环遍历此类指针的容器,其中一些实际上是 Student 而其他则不是)。如果是这样,返回的指针可用于访问它作为 Student对象,具有任何额外的功能/成员可用(以及潜在的限制,如果某些 Record 功能以某种方式隐藏)。

这种方法通常不受欢迎,因为它引出了一个问题“如果我们需要知道“级别”,为什么我们将 Student 视为 Record 而其他 Record 派生类型甚至没有概念等级?”。尽管如此,类似的事情还是时有发生。添加 virtual level函数到 Record如果 Student 也不理想是(其中一个)唯一具有有意义值的派生类:这就是所谓的胖接口(interface)——如果你有一份拷贝,你会在 C++ 编程语言中找到一些关于它们的讨论。

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

基类应该有虚拟析构函数

根据克里斯的评论,您应该添加到 Record :
virtual ~Record() { }

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

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

Student 中推荐的小改进

如果你让 level virtualRecord ,然后让 Student 的读者更清楚类(class)level()virtual 的实现来自基类的函数,您可以使用 override关键字,如果你有一个合适的 C++11-capable 编译器:
int level() override
{
    return level_;
}

如果找不到匹配的(非 const )virtual int level(),这会给你一个编译器错误在一个基类中,所以它可以避免一些偶尔的故障排除。您也可以重复 virtual关键字,如果您认为它具有文档值(value)(尤其适用于 C++03,其中 override 不是一个选项),但它不会产生任何功能差异 - 函数保持 virtual只要它是(隐式或显式)来自基类的虚函数的覆盖。

关于c++ - 无法在 C++ 中调用派生类函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17587458/

相关文章:

c++ - 在多线程 c++0x11 程序中结合 cntr +x 或 kill 信号使用 zmq::poll

c++ - std::unordered_map 初始化

C++ 跨平台库

c++ - 是否有可能每次都在随机地址处加载FORTRAN DLL?

c++ - Xcode clang 编译器中的 unsigned long long 意外行为

python - 如何在 C++ 中使用 ZeroMQ 传送多个图像?

c++ - 如何在 C(++) 中定义自定义错误代码

c++ - 非标准布局类的布局限制

c++ - 构建 NFIQ2 时从 ‘char’ 到 ‘char*’ 的无效转换

c# - 椭圆曲线 Diffie Hellman 公钥大小