I / O操作をサポートするクラスによって提供されるI / O操作インターフェイスは、組み込み型の標準ライブラリiostreamによって定義されるインターフェイスと同じである必要があります。そのため、多くのクラスでは、入力演算子と出力演算子のオーバーロードが必要です。
1.出力演算子のオーバーロード<<
IO標準ライブラリと整合性を保つために、演算子は最初の仮パラメーターとしてostream&を受け入れ、2番目の仮パラメーターとしてクラス型constオブジェクトへの参照を受け入れ、ostream仮パラメーターの参照を返す必要があります!
ostream&演算子<<(ostream&os、const ClassType&object)
{
os << // ....
return os;
}
1. Sales_item出力演算子
ostream&operator <<(ostream&out、const Sales_item&object)
{
out <<オブジェクト。 isbn << '\ t' << object.units_sold << '\ t'
<< object.revenue << '\ t' << object.avg_price();
return out;
}
2、フォーマットされた出力オペレータは、通常、最小限でなければなりませんでした
一般的に、出力される出力オペレータのコンテンツオブジェクトは、最小限の書式設定のために、彼らは改行であるべきではありません!オペレーターのフォーマットを最小限に抑え、ユーザーが出力の詳細を自分で制御できるようにします。
Sales_item item( "C ++ Primer");
cout << item << endl; //ユーザーは出力の改行を制御します
3. IO演算子は非メンバー関数である必要
があります。演算子をクラスのメンバーとして定義することはできません。そうでない場合、左演算子はこのタイプのオブジェクトのみになります:
ostream&Sales_item :: operator <<(ostream&out)
{
out << isbn << '\ t' << units_sold << '\ t' << sales << '\ t'
<< avg_price();
return out;
}
// Test
Sales_item item( "C ++ Primer");
//これ使用法は通常の使用法とまったく反対です
<< cout << endl;
// OR
item.operator <<(cout);
//エラー
cout << item << endl;
通常の使用法をサポートする場合、左のオペランドはostreamタイプです。これは、演算子がクラスのメンバーである場合、それはostreamクラスのメンバーでなければならないことを意味しますが、ostreamクラスは標準ライブラリの一部であり、私たち(およびIO演算子を定義したい人)はライブラリのクラスはメンバーを追加します。
通常、IOオペレーターは非パブリックデータメンバーの読み取りと書き込みを行うため、クラスは通常、IOオペレーターをフレンドとして設定します。
// P437演習14。
フレンドostream&operator <<(ostream&os、const CheckoutRecord&object);
public:
typedef unsigned Date;
// ...
private:
double book_id;
文字列のタイトル。
日付date_borrowed;
日付date_due;
pair <string、string>借り手;
vector <pair <string、string> *> wait_list;
};
ostream&operator <<(ostream&os、const CheckoutRecord&obj)
{
os << obj.book_id << ":" << obj.title << '\ t' << obj.date_borrowed
<< '\ t' << obj。 date_due << '\ t' << obj.borrower.first << ''
<< obj.borrower.second << endl;
os << "
!= obj.wait_list.end ITER(); ITER ++)
{
OS <<(* ITER) - > <<最初の'\ T' <<(* ITER) - > SECOND << ENDL;
}
}
II入力演算子>>のオーバーロードは
、出力演算子に似ています。入力演算子の最初のパラメーターは、読み取りたいストリームへの参照であり、戻り値も同じストリームへの参照です。2番目の仮パラメーターは、読み込まれるオブジェクトへの非const参照です。入力演算子の目的はこのオブジェクトにデータを読み込むことなので、仮パラメーターは非constでなければなりません。
入力演算子は、エラーとファイルの終わりの可能性に対処する必要があります!
1.入力演算子
istream&演算子>>(istream&in、Sales_item&s)
{
double price;
in >> s.isbn >> s.units_sold >> price;
if(in)
{
s.revenue = price * s。 units_sold;
}
else
{
//読み取りが失敗した場合は、オブジェクトをデフォルトの状態にリセットします
s = Sales_item();
}
return in;
考えられるエラーは次のとおりです
。1)指定された値が正しくないため、読み取り操作が失敗する可能性があります。たとえば、isbnを読み込んだ後、入力演算子は次の2つの項目が数値データであることを予期します。数値以外のデータを入力すると、この読み取りとその後のストリームの使用は失敗します。
2)読み取りを行うと、入力ストリームでファイルの終わりやその他のエラーが発生する可能性があります。
ただし、読み込むたびにチェックする必要はなく、読み込んだデータを使用する前に一度チェックするだけです。
if(in)
{
s.revenue = price * s.units_sold;
}
else
{
s = Sales_item();
}
エラーが発生しても、どの入力が失敗したかは関係ありません。代わりに、オブジェクト全体をリセットします!
3.入力エラーの処理入力
が失敗したことを入力オペレーターが検出した場合、オブジェクトが使用可能で一貫していることを確認することをお勧めします。エラーの前にオブジェクトが情報を書き込んでいる場合、これは特に重要です。
たとえば、Sales_itemの入力演算子で、新しいisbnが正常に読み取られ、ストリームエラーが発生する場合があります。isbnを読み込んだ後のエラーは、古いオブジェクトのunits_soldメンバーと収益メンバーが変更されていないことを意味し、その結果、別のisbnをそのデータに関連付けます(悲劇的な...)したがって、仮パラメータを空のSales_itemオブジェクトに返すことで、無効な状態を回避できます。
[ベストプラクティス]
入力演算子を設計するときは、可能であればエラー回復方法を決定することが重要です。
4.エラーを指摘する
発生する可能性のあるエラーの処理に加えて、入力オペレーターは、入力パラメーターの条件付きステータスを設定する必要がある場合もあります。
一部の入力演算子には、追加のチェックが必要です。たとえば、入力オペレーターは、読み取りisbn形式が適切かどうかを確認できます。データを正常に読み取った可能性がありますが、データをISBNとして正しく解釈できません。この場合、実際のIOは技術的には成功していますが、入力オペレーターは条件付きステータスを設定して失敗を示す必要がある場合があります。通常、入力オペレーターはfailbitを設定するだけで済みます。eofbitを設定すると、ファイルが使い果たされ、badbitを設定すると、ストリームが破損している可能性があります。これらのエラーは、IO標準ライブラリに任せて指摘するのが最適です。
// P439演習14.11
クラスCheckoutRecord
{
friend istream&operator >>(istream&in、CheckoutRecord&object);
public:
typedef unsigned Date;
// ...
private:
double book_id;
string title;
Date date_borrowed;
Date date_due;
pair <string、 string>借り手;
ベクトル<pair <string、string> *> wait_list;
};
istream&演算子>>(istream&in、CheckoutRecord&
in >> obj.book_id >> obj.title >> obj.date_borrowed >> obj.date_due;
in >> obj.borrower.first >> obj.borrower.second;
if(!in)
{
obj = CheckoutRecord();
return in;
}
obj.wait_list.clear();
while(in)
{
pair <string、string> * p = new pair <string、string>;
in >> p-> first >> p-> second;
if( in)
{
obj.wait_list.push_back(p);
delete p;
}}
}
return in;
}
3番目の算術演算子
一般に、算術演算子と関係演算子は非メンバー関数として定義されます
。Sales_item演算子+(const Sales_item&lhs、const Sales_item&rhs)
{
Sales_item ret(lhs);
// Sales_item複合コピー演算子を使用してrhs
ret + = rhs;
return ret;
}の値を追加します。
追加演算子は、constオブジェクトへの参照であるオペランドの状態を変更しません。
[ベストプラクティス]
組み込み演算子と整合性を保つために、加算は参照ではなく右辺値を返します。
算術演算子と複合代入演算子のクラスの両方が定義されています。通常、算術演算子の実装には複合代入を使用する必要があります。
// P440問題14.12
(Sales_item&LHS、RHS&Sales_item CONST CONST)Sales_itemオペレータ+
{
Sales_item TMP;
tmp.units_sold + = lhs.units_sold rhs.units_sold;
tmp.revenue + = lhs.revenue rhs.revenue;
戻りTMP;
}
Sales_item Sales_item& ::演算子+ =(const Sales_item&rhs)
{
* this = * this + rhs;
return * this;
}
4番目に、関係演算子
1、等価演算子
対応するすべてのメンバーが等しい場合、2つのオブジェクトは等しいと見なされます。
インライン
ブール演算子==(const Sales_item&lhs、const Sales_item&rhs)
{
return lhs.revenue == rhs.revenue && lhs.units_sold == rhs.units_sold &&
lhs.same_isbn(rhs);
}
インライン
ブール演算子!=(const Sales_ite &lhs、const Sales_item&rhs)
{
return!(lhs == rhs);
}
1)クラスで==演算子が定義されている場合、演算子の意味は2つのオブジェクトに同じデータが含まれていることです。
2)クラスに、その型の2つのオブジェクトが等しいかどうかを判断できる操作がある場合、関数は通常、名前付き関数を作成する代わりに、演算子==として定義されます。ユーザーは==を使用してオブジェクトを比較することに慣れているため、新しい名前を覚えるよりも簡単です。
3)クラスでoperator ==が定義されている場合は、operator!=も定義する必要があります。ユーザーは、あるオペレーターが利用可能であれば、別のオペレーターも存在することを期待します。
4)等値演算子と非演算子は通常、相互に関連して定義する必要があります。一方の演算子に比較オブジェクトの実際の作業を完了させ、もう一方の演算子は前者を呼び出すだけです。
演算子==が定義されたクラスは、標準ライブラリで使用する方が簡単です。findなどの一部のアルゴリズムは、デフォルトで==演算子を使用します。クラスが==を定義している場合、これらのアルゴリズムは、特別な処理なしでそのタイプに使用できます。
2.関係演算子
等価演算子を定義するクラスには、通常、関係演算子もあります。特に、連想コンテナと一部のアルゴリズムは小なり演算子(<)を使用するため、演算子<を定義すると非常に便利です。
<の論理定義が==の論理定義と一致しない場合、この場合、<を定義しない方がよいでしょう。
[注意]
関連するコンテナと特定のアルゴリズムは、デフォルトで<演算子を使用します(ここでは、トランスレータの翻訳は間違っていると思います。オリジナルは... <オペレータbydefult ...を使用します。トランスレータは、デフォルトの<演算子を使用します。しかし、私はデフォルトがより適切だと思います!)。一般に、等値演算子などの関係演算子は、非メンバー関数(「対称」演算子)として定義する必要があります。
C ++プライマーオーバーロード演算子と変換入力/出力、算術/関係
おすすめ
転載: www.cnblogs.com/zhenhua1618/p/12729591.html
ランキング