无法调用C++中的派生类函数
Unable to invoke derived class function in 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
指定的接口。Record
中没有virtual
level()
函数,因此无法通过Record
接口访问Student
的级别函数。只要在Record
中添加这样一个函数,您就可以了:
virtual int level() { return 0; } // Student may override implementation
或
virtual int level() = 0; // Student MUST override implementation
另一种选择:检查记录*是否针对学生
我在上面说。。。
Record
中没有virtual
level()
函数,因此无法通过Record
接口访问Student
的级别函数。
并显示如何添加到Record
接口,但另一种选择是再次访问Student
接口,如:
if (Student* p = dynamic_cast<Student*>(r))
std::cout << "Level " << p->level() << 'n';
第一行检查Record* r
是否恰好指向Student
(当然,在您的代码中总是这样,但假设您在一个接受Record*
的函数中,或者在这样的指针容器上循环,其中一些指针实际上是Student
,而另一些则不是)。如果是这样的话,返回的指针可以作为Student
对象来访问它,并且可以使用任何额外的功能/成员(如果某些Record
功能以某种方式隐藏,则可能存在限制)。
这种方法通常不受欢迎,因为它引出了一个问题:"如果我们需要知道"级别",而其他Record
派生的类型甚至没有级别的概念,为什么我们要将Student
视为Record
?"。尽管如此,类似的事情有时也会发生。如果Student
是唯一一个具有有意义值的派生类,那么在Record
中添加virtual
level
函数也不理想:这就是所谓的胖接口——如果你有副本,你会在C++编程语言中找到一些关于它们的讨论。
(sasha.sochka的回答是第一个提到dynamic_cast
选项的——请投赞成票)
基类应该具有虚拟析构函数
根据chris的评论,您应该添加到Record
:
virtual ~Record() { }
这确保了当派生对象是使用基类指针的delete
d时(例如,如果在main()
的底部添加delete r;
)调用派生类的析构函数实现。(编译器仍然会确保基类析构函数在之后被调用)。
如果你不这样做,那就是未定义的行为,最多你会发现在派生类中添加的任何其他数据成员都没有调用它们的数据成员。。。对于int
来说,这是无害的,但对于std::string
来说,它可能会泄漏内存,甚至持有锁,使程序稍后挂起。当然,依赖最佳情况下的未定义行为不是一个好主意;-)但我认为,除非你制作了基础析构函数virtual
,否则了解肯定不会发生什么是有用的。
学生版的推荐小改进
如果您在Record
中设置level
virtual
,那么为了让Student
类的读者更清楚地了解level()
是基类中virtual
函数的实现,如果您有一个适当的支持C++11的编译器,则可以使用override
关键字:
int level() override
{
return level_;
}
如果在基类中找不到匹配的(非const
)virtual int level()
,这将导致编译器错误,因此可以避免一些偶尔的故障排除。如果您觉得virtual
关键字有文档价值,您也可以重复它(对于C++03来说尤其好,因为override
不是一个选项),但它在功能上没有任何区别——只要它(隐式或显式)覆盖基类中的虚拟函数,该函数就会保持virtual
。
您无法调用r->level()
,因为您正试图调用Record类中不存在的函数。在您的特定情况下,r
指向的数据不仅是Record
,而且同时也是Student
,因此您可以选择:
Student *r = new Student();
cout <<"tuype " << r->getType();
cout <<"Class " << r->level()
或
Record *r = new Student();
cout <<"tuype " << r->getType();
cout <<"Class " << dynamic_cast<Student*>(r)->level()
如果您希望所有Record
都有一个级别,那么您可以添加纯虚拟函数而无需实现。但如果你这样做,你将无法创建类Record
的实例化对象(但你可以初始化它的子类):
class Record { ...
virtual int level() = 0;
}
另一个问题是:您应该将Record
类中的析构函数标记为虚拟的,因为当您使用Student
的delete r
构造函数时,它将不会被调用
因为Record
类对名为level()
的函数一无所知
您需要在基类中创建一个虚拟level()
函数。
在基类中生成类似的虚拟函数
virtual int level() = 0;
一旦你在基类Record中创建了虚拟函数level()
,学生就有必要在学生类中拥有它的函数level()
。目前,Record类中没有虚拟level()
函数,因此,您无法像当前那样使用基类Record访问Student类的level()
函数。
您在Record中缺少虚拟方法级别()声明。顺便说一句,为了防止资源泄漏,您需要在Record中定义虚拟析构函数。
class Record
{
...
public:
virtual ~Record() {}
virtual int level() = 0;
virtual int getType() = 0;
};
学生实例由上传到Record
Record *r = new Student();
当持有学生实例时,代表Record。r->getType()函数调用绑定到带有C++多态性机制的student::getType。要调用level()函数,您可以:
1.向Record类添加一个虚拟函数级别()。2.将r向下转换为Student类,如下所示:
Student *r_new = dynamic_cast<Student>(r);
r_new->level();
编译时,应该会收到类似于以下内容的错误消息:
error C2039: 'level' : is not a member of 'Record'
你需要在你的记录类中添加这个:
virtual int level()=0;
- 如何通过派生类函数更改基类中的向量
- 调用不在基类中的派生类函数而不进行动态强制转换,以最大程度地提高性能
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何使派生类函数在调用时始终调用相同的基类函数?
- 有没有办法在没有虚拟的情况下使用基类指针调用派生类函数
- 如何从派生类函数中调用虚拟函数
- C++通过 const 引用传递时不调用派生类函数
- 如何从包含基类指针的容器中调用派生类函数(基于其类型)?
- 从多重继承中的派生类函数调用适当的父类函数
- 无法访问派生类函数内的基类的受保护数据成员
- 调用不属于基类的派生类函数的最佳方法是什么?
- 如何在C++中正确表达具有相同实现的两个派生类函数?
- 异质集合调用派生类函数 c++
- 如何从指针调用派生类函数到基类类型 (C++)
- 使用抽象指针调用派生类函数
- 通过指针调用基本虚拟方法从派生类函数
- 如何与基类中的派生类函数交朋友
- 如何使派生类函数作为线程的启动例程?
- 使用具有抽象基类指针的映射并调用派生类函数
- 为什么派生类函数参数取基类函数参数的值