繁体   English   中英

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

[英]Segmentation fault when trying to push_back to a std::vector

我有一个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* to 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

然而,问题是我在new_statement->list结尾处推送to_be_pushed的行上遇到Segmentation fault错误。

我已经尝试将这一行分成两部分,我知道问题是当我调用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 

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

Token((void*) list, statement);

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

 Statement() : Token(...)

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

您有两种选择:

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

  2. Statement的构造函数的主体中手动初始化Token 使用Token的默认构造函数,然后在Statement的构造函数体中修复它。

但是,即使您尝试使用#2方法,您也可能会在以后发现其他问题,特别是:您的Statement违反了Rule of Three 这几乎可以确保导致许多难以追踪的错误。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM