尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
Getting use of pure virtual function error when trying to push unique_ptrs to a vector
所以我有一个抽象类,名为MyClassParent,MyClass从中继承。我运行以下代码:
for(auto e:elements){
MyClass m = *this;
MyClass * mpointer = &m;
if(mpointer->xLargerthanY(x,y)){
rv.push_back(unique_ptr<MyClassParent>(mpointer));
if(!rv[0]->solved()) cout<<"true";//works as expected
}
}
rv[0]->solved();//gives pure virtual function called error
奇怪的是,for each循环内部的rv[0]->solved()按预期工作,如果对象的x大于y,则返回true。但如果我从for each环路外部调用函数,我会得到一个名为error的纯虚拟函数,这应该永远不会发生,因为我在子类中重写了solved)。我怀疑这与unique_ptr函数有关,因为我求解的方法不对对象进行任何更改,只返回true或false。
我已经用许多其他方法测试了这一点,它们都在for每个循环中工作,但一旦我退出它,我就会得到一个名为error的纯虚拟函数。
rv[0]->solved();//gives pure virtual function called error
当然。你的程序有未定义的行为,所以它可以做任何事情。将该片段提取为导致问题的原因也相当容易:
MyClass *ptr;
{
MyClass m;
ptr = &m;
}
ptr->solved();
一旦我们消除了所有这些危险,我们就会看到rv
容器中的所有指针都指向具有自动存储持续时间的对象,这些对象已经超出了范围。使用它们访问该对象只会以某种未定义的方式进行操作。
如果您想让rv
存储拥有指向this
副本的指针,那么创建那些具有动态存储持续时间的副本
for(auto e:elements){
MyClass& m = *this; // Assuming we need the reference binding
if(m.xLargerthanY(x,y)){
rv.push_back(make_unique<MyClass>(m));
}
}
现在所有的东西都指向有效的对象。
好的,让我们从一点介绍开始,因为您似乎没有掌握理解智能指针所需的一些东西:
自动存储持续时间:对象的生存期由编译器管理。其生存期由关联变量的范围定义。
例如:
{
X x; // lifetime of x starts here
// ....
} // lifetime of x ends here
动态存储持续时间:对象的生存期由程序员管理。它从对new
的调用开始,并以对delete
的调用结束(这被简化了一点)。
例如:
auto foo(X* x)
{
delete x; // lifetime ends with delete
}
{
X* x = new X{}; // lifetime starts with new
foo(x);
}
在C++中,永远不应该显式调用new
/delete
,而应该使用智能指针。
销毁时的unique_ptr
(除非另有指定)将自动调用其保留的指针上的delete
。这就是必须为其提供一个指向动态对象的指针的原因,即分配有new
。这是你的问题之一。
X x;
std::unique_ptr<X> p{&x};
// p receives a pointer to an automatic storage duration
// this is 100% wrong. The destructor for x would be called twice
// once as part of the automatic lifetime of x
// and then as part of the destructor of p
// resulting in UB
这就是你在这里所做的:
MyClass m = ...;
MyClass * mpointer = &m;
unique_ptr<MyClassParent>(mpointer);
// unique_ptr receives a pointer to an automatic storage duration object
似乎这还不够,您遇到的另一个问题是访问悬挂指针。
m
的范围在的范围内。向量包含指向这些对象的指针,并且这些对象在每次迭代后都会超出范围。执行rv[0]
时,访问的对象的生存期已结束。再次出现未定义的行为。
我希望你能更好地了解unique_ptr
的作用以及它解决了什么问题。正如Storry Teller所展示的那样,解决方案是使用make_unique
。
make_unique
的作用:它调用new
,并根据new
返回的指针创建一个unique_ptr
。你可以自己动手做,但不应该这样做,因为还有其他问题:std::make_unique和std::unique_ptr 之间的差异
正如@StoryTeller所指出的,这是未定义的行为,但让我解释一下为什么它在这种情况下会这样做。由于它是未定义的行为,因此不能保证它在不同的编译器或系统中会以这种方式运行,但我将解释为什么它很有可能:
for(auto e:elements){
MyClass m = *this;
MyClass * mpointer = &m;
if(mpointer->xLargerthanY(x,y)){
rv.push_back(unique_ptr<MyClassParent>(mpointer));
if(!rv[0]->solved()) cout<<"true";//works as expected
}
}
rv[0]->solved();//gives pure virtual function called error
此处
for(auto e:elements){
MyClass m = *this;
....
}
指向CCD_ 20的指针被存储到CCD_。但当m
存在作用域时,对象正在被破坏。该代码隐式调用MyClass::~MyClass()
,它最终替换了对象的虚拟表。首先,派生类被销毁,在销毁的最后一步,虚拟表被替换,这样对象就没有基的虚拟表。在基地,solved()
是纯虚拟的,所以调用:
rv[0]->solved();
因此,调用此函数只能找到基的定义。派生对象已不存在,因此无法使用。在这种情况下,在基类中,resolved()
是纯虚拟的,并且没有实体。这就是它崩溃的原因。如果你的基础中有一个非虚拟的resolved()
,那么你很有可能会发生不同的崩溃,因为对象已经被破坏了。
请注意,即使此代码没有崩溃,稍后当rv
被销毁时,事情也会变得一团糟。rv
内部的指针指向堆栈,但std::unique_ptr
调用delete
并假定对象在堆上。要么它会立即崩溃,因为这是一个非法指针,堆/堆栈将被丢弃,要么它将被简单地忽略。确切的行为是未知的,因为这也是未定义的行为。
当你遇到类似的问题时,这里有一个更简单的例子:
class Base
{
public:
virtual ~Base() { bar(); }
virtual void foo() = 0;
void bar() { foo(); }
};
class Derived: public Base
{
public:
void foo() override { };
};
int main()
{
Derived b;
}
- PowerPC ppc64le上的Gcc Woverloaded虚拟错误
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 纯虚拟类和错误未定义对 'vtable 的引用
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- AWS pandas 安装出现错误:虚拟内存已耗尽:无法分配内存
- 错误消息:不允许抽象类类型 "X" 的对象:纯虚拟"Y"没有覆盖器
- 为什么覆盖非虚拟函数不会导致编译错误
- 与lambda一起使用虚拟继承在初始化列表中捕获此问题的GCC错误
- SQLITE错误:无法重置虚拟机
- 谷神星求解器成本函数继承错误:模板可能不是虚拟的
- C++ 类型特征虚拟示例:编译器错误
- 带有虚拟函数的链接器错误C
- 在模板类中覆盖虚拟函数的错误
- CMake 构建错误 - 虚拟方法错误缺少 vtable
- 排除外部错误R6025-纯虚拟函数调用
- 实现纯虚拟方法后,VTable C 错误
- 当试图检测基类是否具有虚拟破坏者时,如何获取正确的编译器错误消息
- "vtable"链接器错误(涉及带有"=default"的虚拟析构函数) - Clang 3.1 中的潜在错误?
- c++链接错误2019无法解决的外部符号虚拟错误,因为接口…(?)