【问题标题】:Why is my code not setting the variable?为什么我的代码没有设置变量?
【发布时间】:2014-01-26 07:19:11
【问题描述】:

队列的目的是存储指向T 类型对象的指针。当我遍历各种数据结构时,队列用于将对象推入和移出队列。

队列工作正常,除了行:

out = t->payload;

(从底部算起四行)

out 永远不会改变 t->payload 中的值。在调试器中查看它,我可以看到 t->payload 设置正确,但 out 未分配 t->payload 中的值。谁能告诉我这是怎么回事?

template<typename T>
class Queue
{
private:
    struct node
    {
        T           *payload;
        struct node *next;
    };
    node *head;
    node *tail;
public:
    Queue()
    {
            head = new node;
        tail = new node;
        head->next = tail;
        tail->next = tail;
    }
        ...
    void push(T *payload)
    {
        node *newNode;
                node *temp;
        newNode = new node;
        temp = head->next;
        head->next = newNode;
        newNode->next = temp;
        newNode->payload = payload;
    }
    bool pop(T *out)
    {
        node *t;
        node *x;
        t = head;
        x = head;
        if(t == tail)
        {
            return false;  //already empty
        }
        while(t->next != tail)
        {
            x = t;
            t = t->next;
        }
        x->next = tail;
        out = t->payload;
        delete t;
        return true;
    }
}

【问题讨论】:

  • 如何使代码漂亮。这太可怕了。
  • 使用&lt;pre&gt;&lt;/pre&gt;标签
  • 我认为这里的问题是 delete t;在 pop() 中,因为它将调用默认析构函数,它将调用成员上的析构函数.. 意味着 out 指向无效内存.. 我的建议:要么使有效负载成为引用而不是指针,因为不应该无论如何都要改变......或者为你的结构实现一个析构函数,它不会删除有效负载,但将其设置为 nullptr,这意味着其他人(谁?)负责删除。
  • 这个问题的minimal complete example 有十几行。

标签: c++ g++


【解决方案1】:

严格来说,我无法重现您的错误:out 被设置为 t-&gt;payload 的值...在函数内。

bool pop(T *out)
{
  ...
  out = t->payload;
  ...
}

您按值传递指针out。函数修改了指针out,它是一个局部变量,不是你调用函数时它指向的东西,而不是调用代码中可能作为变量存在的指针。 p>

【讨论】:

    【解决方案2】:
    bool pop(T *out)
    T *payload;
    out = t->payload;  
    

    您正在将 T* 复制到 T*,但目标是函数参数,它总是“in”。使用函数结果,或 T**out 或 T * & out。

    【讨论】:

      【解决方案3】:

      out = t-&gt;payload;

      这是您问题的根源。您正在切断传递给函数的参数之间的链接。您的参数out 是指针。如果你想使用这个范例(注意:不是最好的主意),你永远不应该改变out。您应该改为通过

      更改 out 的内容
      *out = *(t->payload);
      

      上面有两个关键问题。如果outt-&gt;payload 是一个空指针,这将发出嘶嘶声。您可以通过传递引用而不是指针来解决前一个问题。如果你这样做,那么你应该使用out = *(t-&gt;payload)。这并不能解决空有效负载的问题。您可以通过确保在添加节点时负载始终为非空来解决此问题。

      但是,问题仍然存在。例如,如果该有效负载指针被删除,使解除引用无效怎么办?使用指针总是有点问题。

      【讨论】:

      • 如果处理得当,指针不是问题;-) 但是在推送中接受指针并在弹出中期待指向的值并不是一个好策略。如果一个指针进去,一个指针必须出来,空指针不会有问题。故事的寓意:永远不要取消引用传递给您的指针,除非合同说它不为空(我们同意)。
      【解决方案4】:

      请注意,如果容器不强制客户端使用指针类型,则容器会更好。下面给出的非常简单的示例避免了签名中的显式星号 (*)。因此,用户可以自行决定是否信任具有普通类型或指针类型的 Keeper。无论哪种方式,数据都只是简单地复制到“keep”实例变量中或从“keep”实例变量中复制出来。如果她决定使用指针,那很好,但分配和释放动态内存的责任在于她。

      template<typename T>
      class Keeper {
      public:
        Keeper() : keeping(false){}
        virtual ~Keeper(){}
        void push( T & payload ){
          if( keeping ) throw "I'm busy";
          keeping = true;
          keep = payload;
        }
        bool pop( T & payload ){
          if( ! keeping ) return false;
          keeping = false;
          payload = keep;
          return true;
        }
      private:
        bool keeping;
        T keep;
      };
      

      【讨论】:

        【解决方案5】:

        尝试在创建新对象时添加一些括号。

        像这样:

        head = new node();
        tail = new node();
        ...
        

        要进一步解释为什么这很重要,请参阅以下链接:

        Do the parentheses after the type name make a difference with new?

        Not using parentheses in constructor call with new (c++)

        编辑:在您的代码中添加了一些打印语句。问题似乎是双重的。

        1. 在推送中,不要将 newNode->payload 设置为有效负载,而是尝试将其设置为 new int(*payload)。这样,值就会被复制。
        2. 在pop结束时,你将out的地址设置为t->payload,但是t之后就被删除了,所以out指向什么。相反,请按上述方式复制该值。

        如果你纠正了这两件事,它似乎有效!

        编辑 2:将其作为参考实现

        bool pop(T &out)
        {
            node *t;
            node *x;
            t = head;
            x = head;
            if(t == tail)
            {
                return false;  //already empty
            }
            while(t->next != tail)
            {   
                x = t;
                t = t->next;
            }
            x->next = tail;
            out = *(t->payload);
            delete t;
            return true;
        }
        

        【讨论】:

        • 感谢您的建议。我将来会这样做。我添加了括号,但 out 仍未设置为 t->payload 值。
        • 检查以上两个建议,它们应该可以解决您的问题;)
        • 请注意,建议 1. 是一个设计决策。要么你想拥有推送到队列中的对象的所有权,要么你不想。重要的是,如果您不拥有所有权(这是有道理的),我建议您将有效负载作为参考来实现。这也意味着您需要在其他地方为您的有效负载调用 delete,否则您有内存泄漏。
        • 1 中给出的建议有效,但这不是我想做的。我不想拥有所有权。你有一个如何实现它作为参考的例子吗?
        • 笼子:是的,同意。 user758362:我在上面提供了一个如何实现它的示例作为参考。记住也要更改第一部分,它应该可以工作!
        猜你喜欢
        • 1970-01-01
        • 2016-04-06
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 2020-10-17
        • 1970-01-01
        • 1970-01-01
        • 2019-10-30
        相关资源
        最近更新 更多