記事ディレクトリ
1.演算子のオーバーロード
以前、C ++関数のポリモーフィズムまたは関数のオーバーロードと呼ばれるものを紹介しました。演算子のオーバーロードはC ++ポリモーフィズムの形式であり、オーバーロードの概念を演算子に拡張して、C ++演算子に複数の意味を与えることができます。
実際、多くのC ++演算子はオーバーロードされています。たとえば、*演算子はアドレスに使用されますが、2つの数値を乗算するためにも使用できます。C ++では、演算子のオーバーロードをユーザー定義の型に拡張できます。たとえば、配列追加クラスなどをオーバーロードできます。ただし、オーバーロードされた演算子は有効なC ++演算子である必要があります。たとえば、「@」を演算子としてオーバーロードすることはできません。
1.1オーバーロードされた加算の例
以下は、クラスの追加とオーバーロードです。
my_class.hコードは次のとおりです
#ifndef MY_CLASS_H
#define MY_CLASS_H
class my_class
{
public:
my_class();
my_class(int h, int m = 0);
my_class operator+(const my_class& t) const;//加法重载
void show();
private:
int hours;
int minutes;
};
#endif // MY_CLASS_H
my_class.cppコードは次のとおりです
#include "my_class.h"
#include<iostream>
using namespace std;
my_class::my_class()
{
hours = minutes = 0;
}
my_class::my_class(int h, int m)
{
hours = h;
minutes = m;
}
my_class my_class::operator +(const my_class& t) const
{
my_class sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes/60;
sum.minutes %= 60;
return sum;
}
void my_class::show()
{
cout<<"After operator +: \n";
cout<<hours<<" : "<<minutes<<endl;
}
主な機能コード
#include"my_class.h"
int main()
{
my_class time1(2, 40);//2小时40分
my_class time2(3, 30);//3小时30分
my_class total;
total = time1 + time2;//重载+,调用
total.show();
}
出力:
After operator +:
6 : 10
上記の式は、3つのオブジェクトに追加することもできます。これは、追加するたびに1つのオブジェクトが返されるためです。
1.2過負荷制限
- オーバーロードされた演算子の少なくとも1つ以上のオペランドはユーザー定義です。たとえば、double型はユーザー定義ではないため、加算演算子をdouble値の合計としてオーバーロードすることはできません。
- 演算子がオーバーロードされている場合、演算子の元の構文規則に違反したり、演算子の優先順位を変更したりすることはできません。これは、元のオペレータールールを保証するためです。
- 新しい演算子を作成できません。
- 次の演算子はオーバーロードできません
- sizeof演算子
- " 。 "メンバー演算子
- スコープ演算子::
- 条件付き演算子?:
- RTTI演算子typeid
- const_cast、dynamic_cast、reinterpret_cast、およびstatic_castのキャスト演算子。
1.3単一のオブジェクトの操作
オブジェクトに倍数を掛けるなど、単一のオブジェクトを操作する演算子のオーバーロードを作成します。そのプロトタイプは次のとおりです。
my_class operator* (double n) const;
定義:
my_class my_class::operator *(double n) const
{
my_class result;
long totalMinutes = hours * n * 60 + minutes * n;
result.hours = totalMinutes/60;
result.minutes = totalMinutes%60;
return result;
}
転送:
total = total * 1.5;//对象扩大1.5倍
total.show();
2.フレンド機能
2.1単一オブジェクトの完璧な操作
パブリックメソッドを使用してプライベートメンバーにアクセスするだけでなく、友達からアクセスすることもできます。フレンドには、フレンド関数、フレンドクラス、フレンドメンバー関数の3種類があります。ここではフレンド関数のみを紹介します。他の2つについては後で説明します。
前のサンプルコードで、オブジェクトに拡張係数を掛ける方法は次のとおりです。
A = B *2.75;
これは実際には次と同等です。
A = B.operator*(2.75);
しかし、A = 2.75 * Bに遭遇した場合、どのように対処すればよいでしょうか。乗算の左側は呼び出し元のオブジェクトである必要があり、2.75はオブジェクトではないため、コンパイラーはメンバー関数呼び出しを使用して式を置き換えることはできません。
そのため、非メンバー関数呼び出しが表示されます。プライベートメンバーにアクセスするために、フレンド関数が表示されます。
friend my_class operator* (double m, const my_class& t);
オーバーロードされた演算子を定義する場合、スコープ修飾子を使用する必要はありません。また、それらを定義するときにフレンドを使用する必要もありません。
my_class operator *(double m, const my_class&t)
{
my_class result;
long totalMinutes = t.hours * m * 60 + t.minutes * m;//注意需要使用t.hours
result.hours = totalMinutes/60;
result.minutes = totalMinutes%60;
return result;
}
主な関数呼び出し:
total = 2 * total;
total.show();
上記のフレンド関数を変更することもできます。
my_class operator* (double m, const my_class& t)
{
return t * m;
}
2.2一般的に使用される友達:オーバーロードされた<<演算子
(1)出力オブジェクト
実際、<<演算子は何度もオーバーロードされており、最初はCおよびC ++演算子の少し左シフトでした。その後、ostreamクラスはそれをオーバーロードし、出力ツールとして使用しました。オブジェクトタイプの出力など、オーバーロードすることもできます。
my_classがオーバーロードに使用される場合、そのオブジェクトは最初のオペランドであり、出力形式はtime << coutになります。これは非常に厄介になります。だから私たちは友達を使って解決します。
friend void operator << (std::ostream& os, const my_class& t);
プロトタイプから、coutが最初の入力オブジェクトであり、my_classが2番目のオブジェクトであることがわかります。my_classのデータメンバーにアクセスする必要があるため、ostreamのフレンドではなくmy_classのフレンド関数を使用する必要があります。 coutのエイリアスを使用します。その定義形式は次のとおりです。
void operator << (std::ostream & os, const my_class& t)
{
os << t.hours <<" hours," <<t.minutes<<" minutes";
}
次に、<<出力オブジェクトを通常どおりに使用できます。
cout<<total;
(2)従来のカウトと組み合わせて使用
上記のフォームは、次のような操作には使用できません。
cout <<"Trip time:" <<total<<endl;
coutは左から右に出力されることがわかっているので、cout << x << y;はそれがと同等であることを意味します
(cout << x)<<y;
したがって、ostreamオブジェクトへの参照を返すために<<演算子を要求する必要があります。つまり、(cout << x)自体はostreamオブジェクトのcoutであるため、左側に配置できます。そのため、フレンド関数を変更し続けます。
//原型修改为:
friend std::ostream& operator << (std::ostream& os, const my_class& t);
//定义修改为:
std::ostream& operator << (std::ostream & os, const my_class& t)
{
os << t.hours <<" hours," <<t.minutes<<" minutes";
return os;
}
2.3オーバーロードされた演算子:メンバー関数または非メンバー関数として
オーバーロードされた演算子の場合、非メンバー関数フォームは、プライベートメンバーにアクセスできるため、通常、フレンド関数フォームを使用します。オブジェクト追加演算子はオーバーロードされており、次の形式でも同じ効果があります。
my_class operator+ (const my_class& t) const;//成员函数形式
friend my_class operator* (const my_class& t1, const my_class& t2);//友元函数形式
加算演算子には2つのオペランドが必要です。メンバー関数バージョンの場合、一方のオペランドはthisポインターを介して暗黙的に渡され、もう一方のオペランドは関数パラメーターとして明示的に渡されます。フレンドバージョンの場合、両方のオペランドがパラメーターとして渡されます。
3.クラスの自動変換と強制型変換
まず、C ++が組み込み型変換を処理する方法を確認しましょう。2つの標準型に互換性がある場合、C ++は値を受信変数の型に自動的に変換します。例えば
long count = 8;//自动将8转换为long
double time = 11;//自动将11转换为double
int side = 3.33;//自动将3.33转换为int
C ++は、次のような互換性のない型を自動的に変換しません。
int *p = 10;//不能进行转换,错误!!!
しかし、C ++では、強制を使用して10をintポインター型にキャストし、ポインターをアドレス10に設定できます。この変換が意味をなすかどうかは別の問題です。
int * p = (int *) 10;
3.1オブジェクトの自動暗黙変換
そのようなコンストラクターがある場合:
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
次に、次のようにコンストラクターを呼び出すことができます。
Stonewt myCat(19.6);
暗黙の変換方法もあります。
Stonewt myCat;
myCat = 19.6;//隐式转换
3つ以上のパラメーターコンストラクターがある場合、暗黙的な変換は使用できません。この種の暗黙的な変換が必要ない場合は、コンストラクターを定義するときにキーワードexplicitを追加して、この自動機能をオフにすることができます。
explicit Stonewt(double lbs);//不允许隐式转换!!!
ただし、明示的な変換、つまり明示的な強制を実行することもできます。
Stonewt myCat;
myCat = Stonewt(19.6);
//或者
myCat = (Stonewt) 19.6;
3.2変換機能
上記はdoubleをStonewtタイプに変換できますが、Stonewtはdoubleタイプに変換できますか?ここでは、C ++の特殊な演算子関数変換関数を使用する必要があります。
変換関数はユーザー定義の強制型変換であり、強制型変換のように使用できます。定義方法:
operator typeName();//typeName为要转换的类型
次の点に注意してください:
- 変換関数はクラスメソッドである必要があります。
- 変換関数は戻り値の型を指定できません。
- 変換関数にパラメーターを含めることはできません。
クラスでintおよびdouble変換関数を定義します。
operator double()const;//转换为double类型
operator int() const;//转换为int类型
彼らの実現:
Stonewt::operator double()const
{
return pounds;
}
Stonewt::operator int()const
{
return int (pounds + 0.5);//pounds为类的数据成员,也就是对象的数据成员
}
main関数で使用します:
int x = int(myCat);
//或者
x = myCat;
cout<<x<<endl;
変換関数が1つだけ定義されている場合は、あいまいさを引き起こす他の変換関数がないため、cout出力オブジェクトと連携することもできます(自動変換)。ただし、変換関数が3つ以上あると、あいまいさが生じ、コンパイルエラーが発生します。
cout << myCat<<endl;//如果没有歧义的情况下,可以使用
long gone = myCat;//如果没有歧义的情况下,可以使用
C ++ 11では、変換関数に明示的を使用します。この場合、変換関数を明示的に使用する必要があります。もちろん、読者は変換関数を使用する代わりに、通常のメンバー関数を自分で作成することもできます。
3.3変換機能とフレンド機能
以下では、Stonewtクラスの加算演算子をオーバーロードします。メンバー関数またはフレンド関数を使用して、次のことを実現できます。
メンバー関数の実装:
Stonewt Stonewt::operator+ (const Stonewt& st)const
{
double pds = pounds + st.pounds;
Stonewt sum(pds);//普通构造函数
return sum;
}
フレンド機能の実装:
Stonewt operator+ (const Stonewt& st1, const Stonewt& st2)
{
double pds = st1.pounds + st2.pounds;
Stonewt sum(pds);//普通构造函数
return sum;
}
加算は上記の方法で実行できます。
Stonewt stone1(9, 12);
Stonewt stone2(12,8);
Stonewt total;
total = stone1 + stone2;
Stonewt(double)コンストラクターが提供されている場合は、次のように実行できます。
Stonewt stone1(9, 12);
double stoneDouble = 12.8;
Stonewt total;
total = stone1 + stoneDouble;//必须是stone1在前面
ただし、フレンド関数のみが次のように動作できます(メンバー関数の場合、最初のパラメーターはオブジェクトである必要があります)。
Stonewt stone1(9, 12);
double stoneDouble = 12.8;
Stonewt total;
total = stoneDouble + stone1;//stone1在后面,不允许!!!
したがって、オブジェクトとdoubleの加算には2つの選択肢があります。1つ目は、フレンド関数として加算をオーバーロードし、Stonewt(double)コンストラクターにdouble型パラメーターをStonewt型パラメーターに変換させることです。2つ目は、前述のdouble型のオーバーロードの追加です。最初のメソッドはコードが少なく、エラーの可能性も低くなりますが、変換コンストラクターが毎回呼び出されるため、時間とメモリのオーバーヘッドが増加します。2番目の方法はコードが多く、プログラマーはより多くの作業を行いますが、計算速度は速くなります。
達成するための2番目の方法:
//成员函数与友元函数的结合方式实现加法重载
Stonewt operator+(double x);
friend Stonewt operator+(double x, Stonewt& s);
概要ディレクトリ
前:(8)オブジェクトとクラス
次:(10)クラスと動的メモリ割り当て
記事の参照:「C ++ Primer PlusSixthEdition」