C++過載插入運算子(<<)和提取運算子(>>)詳解

2020-07-16 10:04:31
必須為任何想和輸出流疊代器一起使用的類型別過載插入和提取運算子。對於自己的類,這是很容易的事。必要的話,可以提供 get 和 set 函數來存取任何 private 或 public 資料成員,或者將運算子函數指定為友元函數。下面是一個簡單的範例,有一個表示姓名的類可以說明:
class Name
{
private:
    std::string first_name{};
    std::string second_name{};
public:
    Name() = default;
    Name(const std::strings first, const std::strings second):first_name{first}, second name {second} {}
    friend std::istream& operator>>(std::istream& in, Name& name);
    friend std::ostream& operator<<(std::ostream& out, const Name& name);
};
// Extraction operator for Name objects
inline std::istream& operator>>(std::istream& in, Name& name)
{ return in >> name.first_name >> name.second_name; }

//Insertion operator for Name objects
inline std::ostream& operator<<(std::ostream& out, const Name& name)
{ return out << name.first_name << ' ' << name.second_name; }
這裡定義了運算子的過載,可以像這個範例一樣,用流疊代器來讀和寫 name 物件:
std::cout << "Enter names as first_name second_name. Enter Ctrl+Z on a separate line to end:n";
std::vector<Name> names {std::istream_iterator<Name> {std::cin},std::istream_iterator<Name>{}};
std::copy (std::begin (names),std::end (names) , std::ostream_iterator<Name> {std::cout," "});
names 容器會被輸入的 Name 物件初始化,直到輸入了 Ctrl+Z 來結束輸入。copy() 演算法會將 Name 物件複製到輸出流疊代器標識的目的位置,這個疊代器會將物件寫到標準輸出流中。我們這裡的名和姓會阻止姓名按指定的寬度列對齊。例如,下面程式碼的效果就不會很好:
for(const auto& name: names)
    std::cout << std::setw(20) << name << std::endl;
有個主意,就是在單一的列中輸出姓名。這個效果也不理想,因為指定的寬度只會被應用到成員 first_name 上。可以通過改變 operator<<() 來做到這一點,因此在輸出姓名之前, 會先將它們連線起來:
inline std::ostream& operator<<(std::ostream& out, const Name& name)
{ return out << name.first_name + ' ' + name.second_name; }
這沒有原始的 << 高效,因為這種方式會生成臨時的 string 物件,但它能夠使前面的迴圈按要求工作。

有時候,可能會想以不同的方式來輸出,這取決於目的位置是不是一個檔案。例如,我們可能會想在標準輸出流的輸出中包含一些在將它們寫入檔案時不想包含的額外資訊。這可以通過檢查 ostream 物件的實際型別來做到:
inline std::ostreams operator<<(std::ostream& out, const Names name)
{
    if(typeid(out) != typeid(std::ostream))
        return out << name.first_name << " " << name.second_name;
    else
        return out << "Name: " << name.first_name << ' ' << name.second_name;
}
現在,當它被寫到 ostream 物件的流時,name 會被帶上字首 "Name: "。在輸出到檔案流或從 ostream 派生的其他流型別中,字首會被省略。