2016-07-26 9 views
1

Я написал C++ код:Как исправить ошибку Core Dumped, когда она выполняется после правильной работы функции?

#include <iostream> 
#include <queue> 
#include <stack> 
#include <vector> 
#include <unordered_map> 
#include <algorithm> 

using namespace std; 

template <class T> 
class TreeNode { 
public: 
    T data; 
    TreeNode<T> *left, *right; 

    TreeNode() { 
     data = {}; 
     left = right = NULL; 
    } 

    TreeNode(T data) { 
     this->data = data; 
    } 
}; 

template <class T> 
class BinaryTree { 
public: 
    TreeNode<T> *root; 

    vector<T> _largestIndependentSet(TreeNode<T> *root) { 
     static unordered_map< TreeNode<T>*, vector<T> > table; 

     if(!root) 
      return {}; 

     if(table.find(root) != table.end()) 
      return table[root]; 

     vector<T> lis = {}, lis_left = {}, lis_right = {}, 
      lis_nrl_left = {}, lis_nrl_right = {}, lis_nrr_left = {}, lis_nrr_right = {}; 

     // Leaf 
     if(!root->left && !root->right) { 
      lis.push_back(root->data); 
     }else{ 
      if(root->left){ 
       lis_left = _largestIndependentSet(root->left); 
       lis_nrl_left = _largestIndependentSet(root->left->left); 
       lis_nrl_right = _largestIndependentSet(root->left->right); 
      } 

      if(root->right){ 
       lis_right = _largestIndependentSet(root->right); 
       lis_nrr_left = _largestIndependentSet(root->right->left); 
       lis_nrr_right = _largestIndependentSet(root->right->right); 
      } 
      if( lis_left.size() + lis_right.size() > 
        lis_nrl_left.size() + lis_nrl_right.size() + 
        lis_nrr_left.size() + lis_nrr_right.size() + 1  ){ // don't keep root 
       lis.insert(lis.end(), lis_left.begin(), lis_left.end()); 
       lis.insert(lis.end(), lis_right.begin(), lis_right.end()); 
      } 
      else { 
       lis.insert(lis.end(), lis_nrl_left.begin(), lis_nrl_left.end()); 
       lis.insert(lis.end(), lis_nrl_right.begin(), lis_nrl_right.end()); 
       lis.insert(lis.end(), lis_nrr_left.begin(), lis_nrr_left.end()); 
       lis.insert(lis.end(), lis_nrr_right.begin(), lis_nrr_right.end()); 
       lis.push_back(root->data); 
      } 
     } 
     cout<<"Calculated Results for: "<<root->data<<": "; 
     for_each(lis.begin(), lis.end(), [](T data) { 
      cout<<data<<" "; 
     }); 
     cout<<"\n"; 
     table[root] = lis; 
     return table[root]; 
    } 

    void largestIndependentSet() { 
     vector<T> lis = _largestIndependentSet(this->root); 
     for_each(lis.begin(), lis.end(), [](T data) { 
      cout<<data<<" "; 
     }); 
    } 
}; 

int main() { 

    BinaryTree<int> bt; 
    TreeNode<int> *root = new TreeNode<int>(10); 
    root->left = new TreeNode<int>(7); 
    root->right = new TreeNode<int>(15); 
    root->left->left = new TreeNode<int>(9); 
    root->left->right = new TreeNode<int>(12); 
    root->right->left = new TreeNode<int>(6); 
    root->right->right = new TreeNode<int>(11); 
    root->left->left->left = new TreeNode<int>(20); 
    root->right->left->right = new TreeNode<int>(5); 
    root->left->left->left->left = new TreeNode<int>(22); 
    root->left->left->left->right = new TreeNode<int>(21); 
    root->right->left->right->left = new TreeNode<int>(4); 
    root->right->left->right->right = new TreeNode<int>(3); 
    bt.root = root; 

    bt.largestIndependentSet(); 
    return 0; 
} 

Я составил его с помощью g++ 5.4.0 на Cygwin:

g++ binary_tree.cpp -std=c++11 

Проблема заключается в том, что после того, как рекурсивная функция _largestIndependentSet() завершается, последняя печать дает мне правильный ответ. Но после этого я получаю эту ошибку: Aborted (core dumped), а печать в largestIndependentSet() не выполняется.

Это непонятно, потому что моя логика кажется правильной. Чем это вызвано?

PS: Если я скомпилировать его с флагом c++14 он прекрасно работает о_О:

g++ binary_tree.cpp -std=c++14 
+2

Вам нужно показать, как создаются указатели, которые добавляются к карте.'vector v = foo (some_apple);' недостаточно информации. Если вы храните указатели на локальные/временные объекты, то эти указатели в какой-то момент станут недействительными. – NathanOliver

+1

Вы пробовали переходить через ваш код с помощью отладчика? –

+0

@NathanOliver Указатели уже существуют (это обычное двоичное дерево). Карта предназначена для хранения дополнительной информации вместе с вектором. Я уверен, что векторы не выходят за рамки. – prakharsingh95

ответ

2

Не вдаваясь в более чем двух основных проблем в размещенном коде.

  • Неопределенные значения указателя на ребенка для конструктора значения T.
  • Отсутствует возвращаемое значение для largestIndependentSet()

Первый из них является обычным явлением, особенно для новичков в C++. Не забудьте оставить ничего с неопределенным значением. В этом случае left и right в конструкторе TreeNode(T value) оставались неопределенными.

Последний из них чрезвычайно важен. Функция largestIndependentSet() утверждает, что она возвращает вектор, но на самом деле не было return. Это также вызывает неопределенное поведение. Оттуда я могу спекулировать, но заметьте, что это все: спекуляция:

Спекуляция: компилятор с радостью сгенерировал код, который в конечном итоге обработал все, что было, находясь в стеке активации, снято вами, как std::vector<T>. Конечно, это неопределенная тарабарщина, потому что вы никогда не возвращали фактический объект. Но вызов не виртуального деструктора std::vector<>, конечно же, не знает, что и при этом обрабатывал то, что, по-видимому, занимало то, что, по его мнению, является его переменными-членами в качестве достоверных данных, когда на самом деле это было совсем не так. Короче говоря, это: взять произвольную память, точку std::vector<> деструкторный код на ней, подставить код и сказать, что память содержит действительный объект std::vector<>, а затем отключает деструктор.

Относительно почему компилятор не ошибка. Ну, часто в C или C++, это просто не ошибка делать что-то неразумное. Компилятор ожидает, что вы достаточно узнаете о том, что делаете, и в этом случае языковых нарушений не было, поэтому оно дало вам преимущество в сомнении. Тем не менее ... Подвергание вашим уровням предупреждений компилятора до высоты педантизма будет для большинства современных компиляторов (icc, gcc, clang и msvc) предупреждает о вашем недостающем возвращаемом значении. Эти предупреждения существуют по какой-то причине, и я решительно поддерживаю их включение и, рассматривая их как ошибки (также вариант компилятора).

+0

На самом деле я инициализировал все указатели на NULL, но потом добавил второй конструктор и забыл скопировать эту строку. Делал C++ достаточно долго, чтобы это знать! – prakharsingh95

+0

@ prakharsingh95 использование список участников инициализация. Это хорошая привычка, и в случае сложных объектов обычно предпочтительнее, поскольку это позволяет избежать ненужной инициализации по умолчанию, 'TreeNode (T const & value): data (value), left(), right() {}' – WhozCraig