C++ 尝试将唯一的\u ptr推送到向量时使用纯虚函数错误

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

所以我有一个抽象类,称为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
用于(自动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;
}