我必须编写一个程序,它的一个函数将通过一个抽象基类返回一个派生类,所以当返回给main的类可以访问派生类的虚方法时。
请记住,我不能改变主程序中的任何内容,因为我不是那个人写的。
#include<iostream>
using namespace std;
class A
{
private:
public:
virtual void DoIt(void)=0;
A(void){};
~A(void){};
};
class B:
public A
{
private:
int Num;
public:
virtual void DoIt(void){Num=7;cout<<"its done";};
B(void){};
~B(void){};
};
A& returnValue(void)
{
B item;
return item;
}
void main()
{
A& item=returnValue();
item.DoIt();
}
当我尝试运行它时,最后一行打破了构建,说DoIt
是纯虚函数调用。
任何想法?
答案 0 :(得分:1)
您正在返回对本地变量的引用,该变量在returnvalue
中的调用完成时被销毁。而是尝试以下方法:
A &returnValue(void) {
return *(new B);
}
int main() {
A& item = returnValue();
item.DoIt();
}
更好的解决方案是返回一个智能指针,让维护主函数的人员对returnvalue
返回的对象的生命周期负责:
#include <memory>
...
std::unique_ptr<A> returnValue(void) {
return std::unique_ptr<A>(new B);
}
int main() {
auto item = returnValue();
item->DoIt();
}
答案 1 :(得分:1)
当函数退出时,item
中返回的returnValue()
将被破坏。该函数返回的是对被破坏对象的引用。你需要以某种方式保留对象。例如:
A& returnValue(void)
{
B *item = new B();
return *item;
}
void main()
{
A& item=returnValue();
item.DoIt();
delete &item; // A's destructor must be virtual for this to work correctly
}
或:
B theItem;
A& returnValue(void)
{
return theItem;
}
void main()
{
A& item=returnValue();
item.DoIt();
}
顺便说一下,您偶然收到“纯虚函数调用”错误:当您调用item.DoIt()
时,虚拟表指针被修改(由B
的析构函数)指向{{1} }的虚拟成员,纯虚函数被存根到显示此错误的函数。但是,您无法保证到达它,因为虚拟表指针已驻留在释放的堆栈内存中。编译器可能已经完全重用了这个内存。
答案 2 :(得分:0)
这里有几个问题。主要的一点是item
中的returnValue
会立即超出范围,因此main中不再有一个对象可以调用DoIt
。因此,没有对象可以跟随继承树。这解释了你得到的错误(main中的item
不再是B类,因为它不存在)。然而,我很惊讶它并不只是崩溃。
要解决此问题,您需要创建对象的副本(通过值而不是引用)或在堆上创建对象(并传递指针)。对于后者:
A* returnValue(void)
{
B* item = new B;
return item;
}
int main()
{
A* item=returnValue();
item->DoIt();
delete item;
}
另一个问题是你有虚拟功能,但没有虚拟析构函数! This can be a serious issue.您希望virtual ~A(void){};
和virtual ~B(void){};
都安全。