尝试将 push_back 到 std::vector 时出现分段错误

use*_*072 4 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
        }
};
Run Code Online (Sandbox Code Playgroud)

然后是类声明:

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

        Statement() {
            list = new std::vector<Token*>;
            Token((void*) list, statement);
        }
};
Run Code Online (Sandbox Code Playgroud)

Basically, when creating a Statement, the inner Token knows it's holding a statement because there is a specific token_type type for that. The inner Token does the cleanup of the vector in its destructor so it has to have a pointer to that vector in its value attribute, but we also have a copy of that pointer in the Statement so we don't need to do the cast from void* to std::vector<Token>* each time;

Now, what I am trying to do is this:

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
Run Code Online (Sandbox Code Playgroud)

The problem, however, is I am getting a Segmentation fault error on the line where I push to_be_pushed at the end of new_statement->list.

I've tried breaking that line in two pieces, and I know the problem is when I am calling list->push_back, not when accessing new_statement->list.

Here's the backtrace I got from 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 
Run Code Online (Sandbox Code Playgroud)

Why is this happening? What am I doing wrong? Is it the code I've posted fault?

Sam*_*hik 5

Token((void*) list, statement);
Run Code Online (Sandbox Code Playgroud)

You are expecting this to call the superclass's constructor. This does not call the constructor. All this does is construct a temporary object, which then gets immediately destroyed. The only way to call the superclass's constructor is in the initialization section of the subclass:

 Statement() : Token(...)
Run Code Online (Sandbox Code Playgroud)

但是,在您的情况下,您需要list在调用超类的构造函数之前初始化子类,即其成员。这并不容易做到。尽管有很多方法可以解决这个问题,但这确实是此类层次结构的基本设计缺陷的症状。

您有两个选择:

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

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

然而,即使您尝试第二种方法,您以后也可能会发现其他问题,特别是:您的Statement违反了“三法则”。这几乎肯定会导致许多难以追踪的错误。

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

有很多方法可以重新设计这个类层次结构,如果没有额外的信息,就不可能建议正确的类设计。