user6245072 user6245072 - 1 year ago 179
C++ Question

Segmentation fault when trying to push_back to a std::vector

I have a

class which is structured as follows:

class Token {
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

Then there's the class Statement:

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

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

Basically, when creating a Statement, the inner Token knows it's holding a statement because there is a specific
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
attribute, but we also have a copy of that pointer in the Statement so we don't need to do the cast from
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;


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

The problem, however, is I am getting a Segmentation fault error on the line where I push
at the end of

I've tried breaking that line in two pieces, and I know the problem is when I am calling
, not when accessing

Here's the backtrace I got from gdb:

#0 0xb6e51410 in memmove ()
from /system/lib/
#1 0x2a0066f8 in std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<Token*>
(__first=0x2a0198d0, __last=0x0,
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,
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,
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=...,
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=...,
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=...,
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

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

Answer Source
Token((void*) list, statement);

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(...)

However in your case, you need to initialize the subclass, namely its list member, before invoking the constructor for the superclass. This cannot be easily done. Although there are ways around this, this is really a symptom of a fundamental design flaw with this class hierarchy.

You have two options:

  1. Reimplement your class hierarchy. The way your classes are designed are fundamentally wrong. Properly-designed C++ code should never need to do tricks like casting to void *.

  2. Initialize Token manually, in the body of the Statement's constructor. Use Token's default constructor, then fix it up in the Statement's constructor body.

However, even if you try the #2 approach, you will likely find other problems later down the road, specifically: your Statement class violates the Rule Of Three. That's almost a guaranteed certainty to cause a number of difficult to track down bugs.

The right answer here will be to step back, and redesign your class hierarchy completely. And getting rid of new allocation in favor of proper usage of C++ library containers would also be a plus.

There are many ways to redesign this class hierarchy, and without additional information it is not possible to suggest the right class design.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download