В моем проекте у меня есть два класса, EarleyParser
учебный класс:
class EarleyParser
{
public:
EarleyParser();
virtual ~EarleyParser();
void initialize( string filePath, bool probabilityParse );
private:
bool probabilityParser;
typedef unordered_map< string, list<Production> > productionHashTable;
productionHashTable earlyHashTable;
};
и Production
учебный класс:
class Production
{
public:
Production();
Production( float productionProbability, int productionLength, vector< string >* productionContent );
Production( const Production& copy_me );
virtual ~Production();
float getProductionProbability();
int getProductionLength();
vector< string >* getProductionContent();
private:
float productionProbability;
int productionLength;
vector< string >* productionContent;
void setProductionProbability( float productionProbability );
void setProductionLength( int productionLength );
void setProductionContent( vector< string >* productionContent );
};
Как вы можете видеть над EarlyParser
класс имеет элемент-член, который является unordered_map
, ключевой элемент которого является строкой, а значение — list
элементов из Production
учебный класс.
Код работает правильно, а unordered_map
а также list
получить, но при вызове стандартного класса деструктора EarleyParser
Я получаю ошибку сегментации.
Как я понимаю деструктор по умолчанию EarleyParser
должен вызвать деструктор по умолчанию unordered_map
который должен назвать один из list
который должен вызывать для каждого из своих элементов деструктор по умолчанию Production
класс, который является следующим:
Production::~Production()
{
if( this->productionContent != NULL )
delete this->productionContent; <- line 44
}
Обратная трассировка с помощью Valgrind и GDB не очень помогла мне решить проблему сегментации, которая описана в EarleyParser.cpp
на линии 44 деструктора.
Должен ли я реализовать класс деструктора, или деструктор по умолчанию будет в порядке?
Любые идеи о том, что может быть причиной ошибки сегментации?
ДОБАВЛЕННЫЙ КОПИЯ КОНСТРУКТОР
Production::Production( const Production& copy_me )
{
if( this->productionContent != NULL )
this->productionContent = NULL;
this->setProductionProbability( copy_me.productionProbability );
this->setProductionLength( copy_me.productionLength );
this->setProductionContent( copy_me.productionContent );
}
Ваше Правило Три не является полным. Поскольку у вас есть член-указатель, вы хотите убедиться, что реализовали конструктор копирования, оператор копирования а также деструктор.
Теперь, потому что у вас есть указатель на vector
член, я собираюсь сказать вам, что вы не должно быть этого, но вместо этого просто есть std::vector<std::string>
или std::unique_ptr<std::vector<std::string> >
,
Я не знаю Зачем вы решили, что нужно держать указатель на контейнер, но это, как правило, не веская причина и подвержено ошибкам.
Вы могли бы держать ссылка в контейнер, но вы должны убедиться, что он инициализирован в ctor.
Проблема с указателями заключается в том, что их слишком легко найти в качестве «решения», но на самом деле они чрезвычайно подвержены ошибкам и их трудно использовать. если ты перестал думать об указателях и перестав быть склонным использовать их на каждом шагу, вам будет намного легче.
Я не вижу никакой инициализации переменной productionContent. Попробуйте инициализировать его в NULL с инициализаторами. Значения по умолчанию для неинициализированных переменных-членов не равны NULL.
Это означает, что productionContent! = NULL всегда будет истинным из-за того, что он начинает отличаться от NULL.
Попробуйте что-то подобное во всех ваших конструкторах:
Production::Production( const Production& copy_me ) : productionContent(NULL)
{
...
Есть два варианта.
Либо вы динамически выделяете вектор в Production
, в этом случае вам нужен оператор присваивания, чтобы сделать глубокую копию векторного указателя. Ваш конструктор копирования должен сделать то же самое. В этом случае вы должны следовать правило трех.
Или вы берете указатель на вектор в Production
конструктор и не выполнять глубокую копию, в этом случае Production
не владеет вектором и не должен удалять его в деструкторе.
Если у вас есть случай 1, я предлагаю сбросить указатель и удерживать std::vector
по значению.
Если у вас есть хорошая или плохая причина для указателя, используйте std :: shared_ptr (в качестве параметра члена и конструктора), чем меньше вы делаете, тем больше вы находитесь. std :: shared_ptr создаст для вас nullptr и удаление!