2015-03-17 5 views
0

Я использую C# в течение года и недавно испытываю терпение в суровом мире C++.C++ Справочная информация

Я пытаюсь создать двоичное дерево, ориентированное на объект. Я прошел через код и прочитал о передаче ссылочного параметра и использовании константы в C++, но не могу решить, что я делаю, чтобы вызвать ошибку нарушения доступа. Я убедился, что структура создана правильно, и код завершает первую строку main, как ожидалось, однако вызов toString, похоже, приводит к ошибке, и я не могу понять, почему.

Вот код до сих пор:

// ExpressionCL.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

using namespace std; 

template<class TData> class TreeNode 
{ 

private: 
    TData Data; 
    const TreeNode<TData>* Left = nullptr; 
    const TreeNode<TData>* Right = nullptr; 

    void setData(TData data) 
    { 
     Data = data; 
    } 

public: 
    TreeNode<TData>(TData data) 
    { 
     setData(data); 
    } 

    TreeNode<TData>(TData data, const TreeNode<TData>& leftNode, const TreeNode<TData>& rightNode) 
    { 
     setData(data); 
     setLeft(leftNode); 
     setRight(rightNode); 
    } 

    void setLeft(const TreeNode<TData>& leftNode) 
    { 
     Left = &leftNode; 
    } 

    void setRight(const TreeNode<TData>& rightNode) 
    { 
     Right = &rightNode; 
    } 

    TreeNode<TData> getLeft() const 
    { 
     if (hasLeft()) 
     { 
      return Left; 
     } 
    } 

    TreeNode<TData> getRight() const 
    { 
     if (hasRight()) 
     { 
      return Right; 
     } 
    } 

    TData getData() const 
    { 
     return Data; 
    } 

    bool hasLeft() const 
    { 
     if (Left != nullptr) 
     { 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    bool hasRight() const 
    { 
     if (Right != nullptr) 
     { 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    string toString() const 
    { 
     string treeString = ""; 
     if (hasLeft()) 
     { 
      treeString += Left->toString(); 
     } 
     treeString += to_string(Data); 
     if (hasRight()) 
     { 
      treeString += Right->toString(); 
     } 
     return treeString; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    TreeNode<int> IntTree(1, TreeNode<int>(1), TreeNode<int>(2)); 
    cout << IntTree.toString() << endl; 
    return 0; 
} 

Некоторые рекомендации или далее рекомендовал ресурсы было бы здорово.

+1

Вы путаетесь между указателями и ссылками> o < – ikh

+0

Хранение указателей на временные часы дадут вам плохое время. –

+0

И код не должен компилироваться так, как написано, по крайней мере, если вы включили предупреждения (и к ошибке), как вам действительно нужно.'getLeft' и' getRight' имеют пути без инструкции 'return'. –

ответ

3

Ваши setLeft и setRight функции установлены сигнальными колокольчиками. Хранение адреса объекта, который был передан по ссылке, серьезно задает проблемы, так как вызывающий может уничтожить объект, а затем вы остаетесь с оборванными указателями на Left и Right.

Фактически это именно то, что вы делаете. Вы передаете временные объекты вашему конструктору, сохраняя их адрес в Left и Right. Затем вы вызываете IntTree.toString(), который пытается использовать указатели для объектов, которые больше не существуют.


Чтобы исправить это, вам необходимо использовать ручное управление жизненным циклом для ваших узлов. Это означает, что узел должен быть создан через new. У вас есть возможность использовать необработанные указатели (в этом случае вы должны тщательно документировать свой интерфейс, чтобы отметить, что вызывающий абонент должен позвонить new, передать указатель и не вызывать delete после).

Другой вариант - использовать интеллектуальные указатели, которые будут отслеживать право собственности на объекты, однако перед этим вы должны решить несколько других проблем.

В частности, treeNode в настоящее время не соответствует Rule of Three. Фиксация этого чрезвычайно важна. Как минимум, disable copying, чтобы вы случайно не делали копии treeNode (которые не будут правильно вести себя до тех пор, пока вы не начнете следовать правилу 3)).

Использование классов умных указателей означает, что вы можете следовать Правилу нуля вместо правила трех, что делает гораздо более чистый код (хотя может быть трудно сделать прямо с места в карьер, если вы новичок в C++, нет любые хорошие онлайн-ресурсы обучения, которые я знаю помимо SO).

+0

Да. В идеале OP нуждается в перемещении ctor на его тип treenode, а затем просто возьмите все по стоимости, но это, вероятно, немного оптимистично, если он только сейчас учится. –

+0

@LightnessRacesinOrbit Да, указатели все равно должны будут использоваться внутри 'treeNode', хотя –

+0

Я где-то читал, что объекты должны передаваться по ссылке, чтобы вы не копировали объект и фактически получали доступ к тому, который первоначально был создан по его адресу что имеет смысл. Я также прочитал, что объекты, созданные через new на C++, должны быть удалены вручную, и поэтому вы создаете их в стеке без новых, чтобы они собирались без ручного управления памятью (RAII или что-то еще). Я подозревал, что аргументы могут выходить за пределы области видимости, но я не могу понять, как методы оценки и мутатора каким-либо другим способом. Это делает какую-то сцену или я неправильно понял что-то фундаментальное. –

0

Вы вызываете конструктор Treenode с временными значениями и сохраняете указатель treenode для этих темпов. После того, как конструктор закончил, эти временные параметры исчезли, и при вызове функции, которая использует указатели для этих темпов (toString), происходит сбой.

Ваш дизайн требует значений treeitems до тех пор, пока используется дерево, поскольку вы храните только указатели на эти значения в дереве. Вы можете изменить дизайн, чтобы хранить копии treenodes внутри дерева.

Смежные вопросы