【问题标题】:What i am making wrong with operator overloading?我对运算符重载做错了什么?
【发布时间】:2019-05-02 06:58:21
【问题描述】:

我不明白当我重载运算符 + 时出了什么问题 (这样做的目的是将 2 个堆栈合并为一个新堆栈)... 它返回“总和”,但会更改先前的值。 ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// /////////////

template <typename T>
classStack
{
    private:

        struct Node
        {
            T data;
            Node *next;
        } *top;

        std::size_t size;

    public:
        Stack();
        ~Stack();
        void            push(T data);
        void            pop(void);
        size_t          get_size(void);
        const Stack&    operator=(const Stack &stack_obj);
        const Stack     operator+(const Stack &stack_obj);
        void            show_all_stack(void);
};


template <typename T>
const Stack<T>  Stack<T>::operator+(const Stack &stack_obj)
{
    Stack   stack;
    Node    *tmp;

    if (!this->size && !stack_obj.size) {
        return stack;
    }
    if (!stack_obj.size)
    {
        stack.size = size;
        stack.top = top;
    }
    else if (!size)
    {
        stack.size = stack_obj.size;
        stack.top = stack_obj.top;
    }
    else
    {
        stack.size = size + stack_obj.size;
        stack.top = new Node;
        stack.top = top;    
        tmp = stack.top;
        while (tmp->next)
            tmp = tmp->next;
        tmp->next = new Node;
        tmp->next = stack_obj.top;
   }
   return stack;
 }

默认构造函数

template <typename T>
Stack<T>::Stack(void): top(nullptr), size(0)
{   
}

析构函数

template <typename T>
Stack<T>::~Stack(void)
{
    Node    *next;

    if (!size)
        std::cout << "Stack is empty!\n";
    else
    {
        while (top != nullptr)
        {
            next = top->next;
            delete top;
            top = next;
        }
        top = nullptr;
    }
}

赋值运算符

template <typename T>
const Stack<T>& Stack<T>::operator=(const Stack<T> &stack_obj)
{
    Node    *tmp;
    Node    *ptr;
    Node    *last;
    Node    *new_node;

    if (&stack_obj != this)
    {
        while (top != nullptr)
        {
            tmp = top;
            top = top->next;
            delete tmp;
        }
        top = nullptr;
        size = stack_obj.size;
        ptr = stack_obj.top;
        while (ptr)
        {
            new_node = new Node;
            new_node->data = ptr->data;
            new_node->next = nullptr;
            if (!top)
            {
                top = new_node;
                last = new_node;
            }
            else
            {
                last->next = new_node;
                last = new_node;
            }
            ptr = ptr->next;
        }
    }
}

【问题讨论】:

  • 我们需要知道Stack&lt;T&gt;的复制语义(复制构造函数、赋值操作符和析构函数)。底线是,如果 Stack&lt;T&gt; 具有不正确或错误的复制语义,则按值返回 Stack&lt;T&gt; 也是错误的。此外,除非复制语义正确,否则不应实现按值返回Stack&lt;T&gt; 的函数(例如您的operator +)。
  • stack.top = top; 刚刚将this 拥有的指针分配给另一个对象。现在两个对象都引用相同的节点。您需要从thisstack_obj 复制节点并将副本插入stack。旁注:stack.top = new Node; 后跟 stack.top = top; 是泄漏。新的Node 丢失了。您想创建新的Nodes,但又不想覆盖指向它们的唯一指针。
  • 有关@Paul 警告的错误的更多信息,请阅读What is The Rule of Three?,然后阅读其朋友Rules of Five and Zero. 您的目标应该是保持资源拥有类符合三或五规则这些课程的用户可以遵守零规则并尽可能保持无知。
  • 今天早些时候提出的类似问题:Why is concatenating two linked lists by assigning pointers wrong?
  • 没有复制构造函数?如果没有,那么你就有麻烦了——operator + 并且任何返回Stack&lt;T&gt; 的函数都被破坏了。其次,当您的赋值运算符必须返回一个值时,它不会返回一个值(您声明它返回一个Stack&lt;T&gt;&amp;)。第三,将该代码移出赋值运算符,并实现一个复制构造函数。然后一旦你有了复制构造函数和析构函数正常工作,赋值运算符变得非常简单。所以总的来说,你的问题与operator + 没有直接关系——你复制的基础被破坏了(正如我在第一条评论中提到的那样)。

标签: c++ templates stack operator-overloading


【解决方案1】:

在创建按值返回Stack&lt;T&gt; 的函数或具有需要Stack&lt;T&gt; 的复制语义的函数(如在您的operator + 中)之前,您需要确保Stack&lt;T&gt; 是可安全复制的。现在,您缺少复制构造函数,因此您的 operator + 将永远无法正常工作,即使函数本身没有错误。

您缺少此功能:

Stack::Stack(const Stack&lt;T&gt;&amp;)

实现复制构造函数的最简单方法是将大部分代码从赋值运算符中取出,并将其放入复制构造函数中。或多或少,代码应该看起来像这样(代码尚未编译或测试——它是为了说明这个答案的要点):

template <typename T>
Stack<T>::Stack<T>(const Stack<T> &stack_obj) : top(nullptr), size(0)
{
    Node    *tmp;
    Node    *ptr;
    Node    *last;
    Node    *new_node;

    ptr = stack_obj.top;
    while (ptr)
    {
        new_node = new Node;
        new_node->data = ptr->data;
        new_node->next = nullptr;
        if (!top)
        {
            top = new_node;
            last = new_node;
        }
        else
        {
            last->next = new_node;
            last = new_node;
        }
        ptr = ptr->next;
    }
}

您已经编写了一个应该可以工作的析构函数,所以我们不会进入那个函数,但是下一步需要一个可以工作的析构函数——赋值运算符的实现。

现在您已经有了复制构造函数和析构函数,现在可以编写赋值运算符了。这里使用的技术是copy / swap idiom

#include <algorithm>
//...
template <typename T>
Stack<T>& Stack<T>::operator=(const Stack<T> &stack_obj)
{
    if ( &stack_obj != this )
    {
        Stack<T> temp(stack_obj);
        std::swap(top, temp.top);
        std::swap(size, temp.size);
    }
    return *this;
}

信不信由你,这行得通。以前在这里的代码被移到了复制构造函数中,因此赋值运算符将利用复制构造函数。

简单地解释一下,代码从传入的stack_obj 创建一个临时的Stack&lt;T&gt; 对象(这就是为什么你需要一个工作副本构造函数)。然后所做的就是将this 成员替换为临时成员。最后,temp 与旧数据一起消失(这就是析构函数需要正常工作的原因)。

请注意,您需要交换所有成员,因此如果您向Stack&lt;T&gt; 类添加更多成员,则需要在赋值运算符中交换它们。

一旦你正确编写了基本的 3 个函数(复制、分配、销毁),那么并且只有这样你才应该编写按值返回 Stack&lt;T&gt; 的函数,或者编写按值接受 Stack&lt;T&gt; 的函数。

您可能对operator + 有其他问题,这些问题超出了此答案提供给您的范围,但要点是您的类要求它在实现+ 之前具有正确的复制语义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-20
    • 2013-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多