C++ 尝试将唯一的\u ptr推送到向量时使用纯虚函数错误
所以我有一个抽象类,称为MyClassParent,MyClass从中继承。我运行以下代码:C++ 尝试将唯一的\u ptr推送到向量时使用纯虚函数错误,c++,inheritance,abstract-class,unique-ptr,virtual-functions,C++,Inheritance,Abstract Class,Unique Ptr,Virtual Functions,所以我有一个抽象类,称为MyClassParent,MyClass从中继承。我运行以下代码: for(auto e:elements){ MyClass m = *this; MyClass * mpointer = &m; if(mpointer->xLargerthanY(x,y)){ rv.push_back(unique_ptr<MyClas
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
用于(自动e:元素){
MyClass m=*这个;
MyClass*mpointer=&m;
if(mpointer->xLargerthanY(x,y)){
rv.推回(唯一的ptr(mpointer));
如果(!rv[0]->solved())不能
rv[0]->solved();//给出称为error的纯虚拟函数
当然有。您的程序有未定义的行为,所以它可以做任何事情。也可以很容易地将该片段提取到导致问题的原因中:
MyClass *ptr;
{
MyClass m;
ptr = &m;
}
ptr->solved();
一旦我们消除了所有这些杂七杂八的东西,我们就会看到rv
容器中的所有指针都指向具有自动存储持续时间的对象,这些对象已经超出了范围。使用它们访问该对象只会以某种未定义的方式运行
如果希望rv
存储指向此
副本的指针,请创建具有动态存储持续时间的副本
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));
}
}
用于(自动e:元素){
MyClass&m=*this;//假设我们需要引用绑定
if(m.xLargerthanY(x,y)){
rv.向后推_(使_唯一(m));
}
}
现在所有的东西都指向有效的对象。好的,让我们从一个小介绍开始,因为您似乎没有掌握理解智能指针所需要的一些东西:
自动存储持续时间:对象的生存期由编译器管理。其生存期由关联变量的范围定义
例如:
动态存储持续时间:对象的生存期由程序员管理。它从调用new
开始,到调用delete
结束(这稍微简化了一点)
例如:
<>在C++中,你不应该显式调用<代码>新< /COD> >代码>删除< /代码>,并使用智能指针代替.< /P>
销毁时,unique\u 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
X;
std::unique_ptr p{&x};
//p接收指向自动存储持续时间的指针
//这是100%错误的。x的析构函数将被调用两次
//一次作为x的自动生存期的一部分
//然后作为p的析构函数的一部分
//导致UB
这就是你在这里所做的:
MyClass m = ...;
MyClass * mpointer = &m;
unique_ptr<MyClassParent>(mpointer);
// unique_ptr receives a pointer to an automatic storage duration object
MyClass m=。。。;
MyClass*mpointer=&m;
唯一的ptr(mpointer);
//unique_ptr接收指向自动存储持续时间对象的指针
似乎这还不够,您遇到的另一个问题是访问一个悬挂的指针
m
的作用域在for内。向量包含指向此类对象的指针,并且这些对象在每次迭代后都会超出作用域。执行rv[0]
操作时,您将访问其生存期已结束的对象。请再次访问未定义的行为
我希望您能更好地理解unique\u ptr
的作用和解决的问题。正如Storry Teller所示,解决方案是使用make\u unique
make_unique
的作用是:它调用new
并从new
返回的指针创建一个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
指向m
的指针存储在rv
向量中。但是当m
存在作用域时,对象将被销毁。代码隐式调用MyClass::~MyClass()
,它最终将替换对象的虚拟表。首先,将销毁派生类,在销毁的最后一步,将替换虚拟表,以便对象no具有基的虚拟表。在基中,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;
}
提示:什么是唯一的\u ptr
呢?它做了什么,而原始指针没有呢?@juanchopanza我知道当某些内容超出范围时,它会自动删除,但我检查了for each循环外的rv[0]与for each循环内的rv[0]仍然具有相同的内存地址。另外,知道对此的任何修复,我需要能够调用solved()循环之外。两个独立的东西在管理同一个对象的寿命。这永远不会有好的结局。MyClass m=*this;
创建存储在局部变量中的这个对象的副本,而MyClass*mpointer=&m;
存储指向该变量的指针。这样的对象()
for(auto e:elements){
MyClass m = *this;
....
}
rv[0]->solved();
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;
}