C++ 为啥在向量段错误中放置对象?

Posted

技术标签:

【中文标题】C++ 为啥在向量段错误中放置对象?【英文标题】:C++ Why emplacing object in vector segfaults?C++ 为什么在向量段错误中放置对象? 【发布时间】:2019-03-27 01:31:22 【问题描述】:

我想创建一个“Act”对象向量,其中包含指向“Eat”或“Drink”动态分配对象的指针。新对象的放置方式如下:

action_vector.emplace_back(Act::BehaviorType::eat);

但是,这是段错误,我不知道为什么。我认为 emplace_back 会隐式调用移动构造函数,而不是析构函数,但出于某种原因,它是(我认为)是什么搞砸了一切。

有什么方法可以成功创建此类对象的向量?

以下是其余代码及其输出。对不起,如果它有点冗长,但基本上它只是一个策略模式。

#include <iostream>
#include <vector>

class IBehavior

public:
    IBehavior() = default;
    virtual ~IBehavior() = default;
    virtual void execute() = 0;
;

class Drink : public IBehavior

public:
    Drink(): IBehavior() 
    ~Drink() 
    void execute()  std::cout << "Drinking" << std::endl; 
;

class Eat : public IBehavior

public:
    Eat(): IBehavior() 
    ~Eat() 
    void execute()  std::cout << "Eating" << std::endl; 
;


class Act

    IBehavior * b;

public:

    enum class BehaviorType  eat = 0, drink = 1 ;

    Act() = default;
    ~Act()
    
        std::cout << "Calling the destructor" << std::endl;
        delete b;
    
    Act(BehaviorType b_type)  SetBehavior(b_type); 

    Act(Act&& act)
    
        std::cout << "Calling the move constructor" << std::endl;
        this->b = act.b;
    


    void SetBehavior(BehaviorType b_type)
    
        if(b_type == BehaviorType::eat) b = new Eat();
        if(b_type == BehaviorType::drink) b = new Drink();
    

    void execute()  b->execute(); 
;


int main(int argc, char * argv[])

    std::vector<Act> action_vector;

    for(int i = 0; i < 10; ++i)
    
        action_vector.emplace_back(Act::BehaviorType::eat);
        action_vector[i].execute();
    

    return 0;

输出:

Eating
Calling the move constructor
Calling the destructor
Eating
Calling the move constructor
Calling the move constructor
Calling the destructor
Calling the destructor
Segmentation fault: 11

【问题讨论】:

当移动构造函数或移动赋值“窃取”一次性资源时,它不能将其留在原始对象中以供析构函数获取。所以在这种情况下,将指针b复制到thisthis-&gt;b = act.b)后,应该设置act.b = nullptr 【参考方案1】:

你的移动构造函数复制b,而析构函数删除b,所以如果你移动构造一个实例,那么相同的指针值将被删除两次,这具有未定义的行为。

一般解决方案:使用智能指针。


另一个错误:默认构造函数未初始化 b。当一个默认构造的对象被销毁时,未初始化的指针被删除并且行为未定义。智能指针也解决了这个问题。

【讨论】:

Mac 上没有段错误,LLVM 版本 10.0.0 (clang-1000.10.44.2) @Gardener 好吧,行为未定义。不保证会出现段错误。 谢谢,这很好。但是,如果我写“this->b = std::move(act.b);”,问题仍然存在在移动构造函数中(这是因为“原始” b 仍然被破坏但现在是空的吗?)。我可能应该使用智能指针,但我真的很想先用常规指针来修正我的直觉。使用常规指针如何处理这种情况? 移动裸指针与复制指针相同。它解决不了任何问题。您可以通过分配 act.b = nullptr 来修复此错误。 谢谢,我明白了,TIL 删除 nullptr 也是安全的。

以上是关于C++ 为啥在向量段错误中放置对象?的主要内容,如果未能解决你的问题,请参考以下文章

在类对象段错误中使用 boost::interprocess,为啥?

c++ 类向量中的段错误

全局向量在 C++ 程序结束时导致段错误

C++:大的多维向量导致段错误

为啥这个非常简单的构造函数会导致段错误?

为啥大型静态数组会产生段错误而动态却不会? (C++)