尝试将push_back转换为std :: vector时出现分段错误

时间:2016-10-09 16:44:52

标签: c++ pointers vector segmentation-fault

我有一个Token类,其结构如下:

class Token {
    public:
        void* value;
        token_type type; // enum to know which type of data is it storing inside value
        unsigned int line;

        Token() = default;

        Token(void* new_value, token_type new_type):
            value(new_value), type(new_type)
            {}

        ~Token() {
            //free the memory occupied by the object pointed to by value based on it's type
            //this also handles the case of the Token being an instantiation of Statement
        }
};

然后是类声明:

class Statement: public Token {
    public:
        std::vector<Token*>* list;
        unsigned int lenght = 0;

        Statement() {
            list = new std::vector<Token*>;
            Token((void*) list, statement);
        }
};

基本上,在创建Statement时,内部令牌知道它持有一个语句,因为它有一个特定的token_type类型。内部令牌在其析构函数中对向量进行清理,因此它必须在其value属性中有一个指向该向量的指针,但我们在Statement中也有该指针的副本,所以我们不要这样做每次都需要从void*std::vector<Token>*进行演员投放;

现在,我要做的是:

std::string* value = new std::string("Sample text");
Token* to_be_pushed = new Token((void*) value, string); //the object pointed to by value will be deleted in this Token's destructor

Statement* new_statement = new Statement;

new_statement->list->push_back(to_be_pushed);

delete new_statement; //Token destructor gets called; It knows it's a statement,
//so it knows value is pointing to a std::vector<Token*> object, and it deletes each pointer in that vector and then the vector itself

问题是,我在to_be_pushed末尾推送new_statement->list的行上收到了分段错误。

我已尝试将这一行分成两部分,我知道问题出在我打电话list->push_back时,而不是在访问new_statement->list时。

这是我从gdb获得的回溯:

#0  0xb6e51410 in memmove ()
from /system/lib/libc.so
#1  0x2a0066f8 in std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<Token*>
(__first=0x2a0198d0, __last=0x0,
__result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:378
#2  0x2a006640 in std::__copy_move_a<true, Token**, Token**> (__first=0x2a0198d0, __last=0x0,
__result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:395
#3  0x2a007070 in std::__copy_move_a2<true, Token**, Token**> (__first=0x2a0198d0, __last=0x0,
__result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:432
#4  0x2a007010 in std::copy<std::move_iterator<Token**>, Token**> (__first=..., __last=...,
__result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:464
#5  0x2a006fb0 in std::__uninitialized_copy<true>::__uninit_copy<std::move_iterator<Token**>, Token**> (__first=..., __last=...,
__result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:93
#6  0x2a006f70 in std::uninitialized_copy<std::move_iterator<Token**>, Token**> (__first=...,
__last=..., __result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:123
#7  0x2a006eec in std::__uninitialized_copy_a<std::move_iterator<Token**>, Token**, Token*> (
__first=..., __last=...,
__result=0x2a019908)
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:279
#8  0x2a006dc0 in std::__uninitialized_move_if_noexcept_a<Token**, Token**, std::allocator<Token*> > (__first=0x2a0198d0, __last=0x0,
__result=0x2a019908, __alloc=...)
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:300
#9  0x2a007264 in std::vector<Token*, std::allocator<Token*> >::_M_emplace_back_aux<Token* const&> (this=0x2a019908,
__args=@0x2a0198e8: 0x2a0198d0)
at /data/data/com.termux/files/usr/include/bits/vector.tcc:457
#10 0x2a005b70 in std::vector<Token*, std::allocator<Token*> >::push_back (this=0x2a019908,
__x=@0x2a0198e8: 0x2a0198d0)
at /data/data/com.termux/files/usr/include/bits/stl_vector.h:1049 

为什么会这样?我究竟做错了什么?这是我发布的错误代码吗?

1 个答案:

答案 0 :(得分:4)

Token((void*) list, statement);

您希望这可以调用超类的构造函数。这不会调用构造函数。所有这一切都是构造一个临时对象,然后立即被销毁。调用超类的构造函数的唯一方法是在子类的初始化部分:

 Statement() : Token(...)

但是在您的情况下,在调用超类的构造函数之前,需要初始化子类,即其list成员。这不容易做到。尽管存在各种方法,但这实际上是这种类层次结构的基本设计缺陷的症状。

您有两种选择:

  1. 重新实现您的类层次结构。你的课程设计方式从根本上说是错误的。正确设计的C ++代码永远不需要像转换为void *这样的技巧。

  2. Token构造函数的正文中手动初始化Statement。使用Token的默认构造函数,然后将其修复到Statement的构造函数体中。

  3. 但是,即使您尝试使用#2方法,您也可能会在未来发现其他问题,特别是:Statement班级violates the Rule Of Three。这几乎可以确保导致许多难以追踪的错误。

    这里的正确答案将是退后一步,并完全重新设计您的类层次结构。摆脱new分配以支持正确使用C ++库容器也是一个优势。

    有许多方法可以重新设计此类层次结构,如果没有其他信息,则无法建议正确的类设计。