【问题标题】:Linked list template implementation and operators overloading链表模板实现和运算符重载
【发布时间】:2021-05-07 23:12:22
【问题描述】:

我对 C++ 中的模板非常陌生,目前我在理解它们时遇到了一些麻烦。所以,有人给了我下一个使用模板的链表实现,而运算符重载对我来说非常不清楚。代码如下:

#include <iostream>
using namespace std;

template<typename T> class List;

template <typename T>
ostream& operator<< (ostream&, const List<T>&);

template <typename T>
struct Node {
    T info;
    Node<T>* next;

    Node (T x, Node<T>* n = nullptr) : info(x), next(n) {}
};

template <typename T>

class List {
    Node<T>* first, *last;
    public:
        List() {
            this->first = nullptr;
            this->last = nullptr;
        }

        List (initializer_list<T> l)
        {
            ...
        }
         ~List();

        template <typename U>
                friend istream& operator >>(istream& is, List<U>& l);  /// ***HERE***

        friend ostream& operator<< <T> (ostream&, const List<T>&);     /// ***HERE***
        void insert(T,unsigned);
};
template <typename T> List <T>::~List()
{
   ...
}

template <typename T>
istream& operator >>(istream& is, List<T>& l)
{
    Node<T>* f = l.first;
    while (f != nullptr)
    {
        is >> f->info;
        f = f->next;
    }
    return is;
}

template <typename T>
ostream& operator<< (ostream& out, const List<T>& l) {
    Node<T>* p = l.first;

    while (p != nullptr) {
        out << p->info << " ";
        p = p->next;
    }

    return out;
}
template<typename T>
void List<T>::insert(T t, unsigned x)
{
    ...
}

int main () {
}

他为什么使用template &lt;typename U&gt; 来重载&gt;&gt;friend ostream&amp; operator&lt;&lt; &lt;T&gt; (我什至不知道&lt;T&gt; 右边的&lt;T&gt; 是什么意思)。

【问题讨论】:

  • ostream&amp; operator&lt;&lt; &lt;T&gt; (ostream&amp;, const List&lt;T&gt;&amp;); 是上面声明的模板的实例化。类似于拥有template &lt;typename T&gt; void foo();,然后是foo&lt;T&gt;();。为什么作者不为operator&gt;&gt;做同样的事情不清楚,你要问作者。
  • 你需要好好学习一下C++编程语言的参考书。你可以从:cplusplus.com/doc/oldtutorial/templates

标签: c++ templates linked-list operator-overloading


【解决方案1】:

我想你的问题的答案可以在C++ Templates: The Complete Guide第二版的2.4 Friends第30页找到:

通常operator &gt;&gt;&lt;&lt; 对于标准输入std::istream 和输出流std::ostream 是重载的,因此您可以从输入读取并写入抽象输出流。这可以在类之外完成,但如果在进程中访问的类成员是 private (如您的示例中的两个指针 firstlast),要么必须使用 setter 和 getter 为其创建一个接口,或者在类中将运算符声明为friend。通常人们会在类本身中声明和定义它,但如果出于某种原因希望或需要做其他事情,事情会变得有点棘手,并且有两种主要方法声明友元函数但随后定义它

  • 可以声明具有不同模板参数的新函数模板

    template <typename U>
    friend istream& operator >> (istream&, List<U>&);
    

    这就是friend istream&amp; operator &gt;&gt; (istream&amp;, List&lt;U&gt;&amp;); 的实现方式。

  • 可以前向声明类以及输出运算符

    template<typename T> class List;
    
    template <typename T>
    ostream& operator << (ostream&, const List<T>&);
    

    然后我们将非成员函数模板的特化声明为friend(因此使用&lt;T&gt;)。

    friend ostream& operator << <T> (ostream&, const List<T>&);
    

    这就是friend ostream&amp; operator &lt;&lt; &lt;T&gt; (ostream&amp;, const List&lt;T&gt;&amp;);的实现方式。

在这两种情况下,您都必须通过类外的定义来跟进声明。

我没有从代码 sn-p 中看到一个真正的理由来选择一个而不是另一个:在这种情况下,它们应该是平等的。我个人实际上什至不会经历前向声明任何东西的麻烦,而是会在类本身中声明和定义它,因为我认为它更清楚。在我看来,应尽可能避免前向声明。

【讨论】:

  • “应尽可能避免前向声明。”.. 好吧,如果可以用前向声明替换包含标题,那么应该这样做。如果一个都不需要,那就更好了
猜你喜欢
  • 2017-11-26
  • 2013-03-05
  • 1970-01-01
  • 2013-03-21
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
相关资源
最近更新 更多