我有一个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
为什么会这样?我究竟做错了什么?这是我发布的错误代码吗?
答案 0 :(得分:4)
Token((void*) list, statement);
您希望这可以调用超类的构造函数。这不会调用构造函数。所有这一切都是构造一个临时对象,然后立即被销毁。调用超类的构造函数的唯一方法是在子类的初始化部分:
Statement() : Token(...)
但是在您的情况下,在调用超类的构造函数之前,需要初始化子类,即其list
成员。这不容易做到。尽管存在各种方法,但这实际上是这种类层次结构的基本设计缺陷的症状。
您有两种选择:
重新实现您的类层次结构。你的课程设计方式从根本上说是错误的。正确设计的C ++代码永远不需要像转换为void *
这样的技巧。
在Token
构造函数的正文中手动初始化Statement
。使用Token
的默认构造函数,然后将其修复到Statement
的构造函数体中。
但是,即使您尝试使用#2方法,您也可能会在未来发现其他问题,特别是:Statement
班级violates the Rule Of Three。这几乎可以确保导致许多难以追踪的错误。
这里的正确答案将是退后一步,并完全重新设计您的类层次结构。摆脱new
分配以支持正确使用C ++库容器也是一个优势。
有许多方法可以重新设计此类层次结构,如果没有其他信息,则无法建议正确的类设计。