【问题标题】:C++ Reference passingC++ 参考传递
【发布时间】:2015-03-17 11:49:15
【问题描述】:

我已经使用 C# 大约一年了,最近一直在测试我对 C++ 严酷世界的耐心。

我正在尝试创建一个面向对象的二叉树。我已经逐步完成了代码并阅读了 C++ 中引用参数传递和 const 的使用,但无法弄清楚我正在做什么来导致访问冲突错误。我已确保正确创建了结构,并且代码按预期完成了 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;
}

一些指导或进一步推荐的资源会很棒。

【问题讨论】:

  • 你对指针和引用感到困惑>o
  • 存储指向临时对象的指针会给你带来麻烦。
  • 并且代码不应该按照编写的那样编译,至少如果你打开了警告(和错误),你真的应该这样做。 getLeftgetRight 的路径没有 return 语句。
  • @ChristopherCreutzig 返回失败不需要编译错误或警告。当然,OP 仍然应该修复这些功能。
  • 另一件事是hasLeft() 可以写成return Left != nullptr;。这将大大提高代码的可读性。

标签: c++ pointers reference


【解决方案1】:

您的setLeftsetRight 函数敲响了警钟。存储通过引用传递的对象的地址是很麻烦的,因为调用者可能会破坏该对象,然后你就剩下LeftRight 的悬空指针。

事实上,这正是你所做的。您将临时对象传递给您的构造函数,将它们的地址存储在LeftRight 中。然后调用IntTree.toString(),它会尝试使用指向不再存在的对象的指针。


要解决此问题,您需要对节点使用手动生命周期管理。这意味着必须通过new 创建节点。您可以选择使用原始指针(在这种情况下,您需要仔细记录您的接口,注意调用者应该调用new,传入指针,而不是之后调用delete)。

另一种选择是使用智能指针来跟踪对象的所有权,但是在此之前您还有一些其他问题需要解决。

具体来说,treeNode 目前不关注Rule of Three。解决这个问题非常重要。至少,disable copying,这样您就不会意外复制treeNode(在您开始遵循三规则之前,它不会正常运行))。

使用智能指针类意味着您可以遵循零规则而不是三规则,这使得代码更加简洁(尽管如果您是 C++ 新手,可能很难立即完成,但没有任何除了 SO 之外我所知道的很好的在线教学资源。

【讨论】:

  • 是的。理想情况下,OP 需要在他的树节点类型上添加一个移动 ctor,然后按值获取所有内容,但如果他刚刚学习,这可能有点乐观。
  • @LightnessRacesinOrbit 是的,指针仍然需要在treeNode 中使用
  • 我在某处读到,对象应该通过引用传递,这样您就不会最终复制对象,而是访问最初由它的地址创建的对象,这是有道理的。我还读过在 C++ 中通过 new 创建的对象必须手动删除,因此您在没有 new 的情况下在堆栈上创建它们,以便在没有手动内存管理(RAII 或其他东西)的情况下收获它们。我确实怀疑这些论点可能超出了范围,但我看不出如何以任何其他方式评估评估器和变异器方法。这会造成任何场景还是我误解了一些基本的东西。
  • @EdwardJBrown 您希望您的堆栈对象在这里“收获”,因为您希望它们在您的树中存活。临时对象 TreeNode&lt;int&gt;(1) 等在以下 ; 之后被销毁,在下一行之前。 TreeNode 结构应设计为拥有其子级的所有权,因此不适合使用堆栈对象。 (LRO 指出,如果您实现移动语义,那么您可以使用堆栈对象调用您的函数,并且树将通过使用移动操作创建新的手动管理对象,但在此阶段不是必需的)。
  • 是的,treeNode 的析构函数必须是 delete 它的子级。
【解决方案2】:

您正在使用临时值调用 Treenode 构造函数,并将指向这些临时值的 treenode 指针存储在树节点中。 构造函数完成后,这些临时文件消失了,在调用使用指向这些临时文件的指针(toString)的函数时,正在发生崩溃。

只要使用树,您的设计就需要树项的值,因为您只在树中存储指向这些值的指针。 您可以更改设计以在树中存储树节点的副本。

【讨论】:

    猜你喜欢
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 2019-12-28
    相关资源
    最近更新 更多