我确定我错过了代码的某些部分。
我有以下代码:
#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 inRecord
, so you can not accessStudent
's level function through theRecord
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
virtual
在 Record
,然后让 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/