ところで、質問がされ、それがどれほど基本的なものであるかということで、私は非常に単純化した (かなり非公式であり、それほど衒学的ではありませんが) 回答をしようと思います。
オーバーロードされたオペレーターが 2 つのパラメーターを取り、1 つが os オブジェクトである理由がわかりません
operator<< は二項演算子です。左側と右側があります。あなたが書くとき:
cout << 123;
2 つのオペランド (引数) を使用してこの演算子を呼び出しています: 左側の 'cout' と右側の整数 '123'。
実際のリンク リスト オブジェクトを main.cpp に出力するときは、os オブジェクトを渡す必要はありませんでした。
印刷関数は、クラスのメンバー関数または演算子です。リストオブジェクトに対して使用する「this」ポインターが既にあるため、大雑把に言えば、最初の引数を明示的に渡す必要はないと暗黙的に推測します。左側のオペランドに対して既に動作する暗黙的に推定された「this」オブジェクトがないため、非メンバー演算子の場合はそうではありません。
次のようなコードを書くと:
my_list.print(cout);
これは、'my_list' と 'cout' という 2 つの引数を実際に渡すものと考えることができます。明示的に記述しなくても、「this」を介して「my_list」とそのメンバーにアクセスできます。次のように、print 関数を非メンバーとして記述した場合は、そうではありません。
template <class T>
void print(const List<T>& my_list, ostream& os);
これは、メンバー関数ではない演算子にも当てはまります。
また、なぜosを返すのですか?
ostream への参照を返すことで、次のようなステートメントを記述できます。
cout << "hello " << "world";
まず、operator<<(cout, "hello ") を呼び出します。これにより、別の ostream 参照が提供されます。これにより、operator<<(cout, "world") の呼び出しに進むことができます。たとえば、void が返された場合、左側のオペランドとして void を使用して「world」を出力しようとするため、1 つのステートメントでその演算子を 2 回呼び出すことはできません。
"os <<" の代わりに cout を使用できないのはなぜですか?
cout は基本的に ostream インターフェイスを実装しています。ofstream、ostringstream、およびその他のタイプの出力ストリームも同様です。特定の ostream の派生物ではなく、必要な基本的なインターフェイスの観点から記述することで、記述したコードを stdio ストリーム、ファイル ストリーム、ストリーム ストリームなどで動作させることができます。基本的に、それはあなたのコードを非常に一般的で再利用可能なものにします。ポリモーフィズムの概念に取り組むにつれて、この主題についてさらに学ぶことができます。