C++ のオーバーロードされた演算子とオーバーロードされた関数
C++ のオーバーロードされた演算子とオーバーロードされた関数
C++ では、関数と演算子の複数の定義を同じスコープで指定できます。これは、それぞれ関数のオーバーロードと演算子のオーバーロードと呼ばれます。
オーバーロードされた宣言は、スコープ内で既に宣言されている関数またはメソッドと同じ名前の宣言ですが、パラメーター リストと定義 (実装) が異なります。
オーバーロードされた関数またはoperatorを呼び出すと、コンパイラは、使用するパラメーターの型と定義内の型を比較して、最も適切な定義を使用することを決定します。最も適切なオーバーロードされた関数または演算子を選択するプロセスは、オーバーロードの解決と呼ばれます。
C++ での関数のオーバーロード
同じスコープ内で、同じ名前の複数の関数を同様の関数で宣言できますが、同じ名前のこれらの関数の仮パラメーター (パラメーターの数、型、または順序) は異なる必要があります。戻り値の型が異なるだけで関数をオーバーロードすることはできません。
次の例では、同じ名前の関数 print() を 使用して、異なるデータ型を出力しています。
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "Printing int: " << i << endl;
}
void print(double f) {
cout << "Printing float: " << f << endl;
}
void print(string c) {
cout << "Printing character: " << c << endl;
}
};
int main(void)
{
printData pd;
// Call print to print integer
pd.print(5);
// Call print to print float
pd.print(500.263);
// Call print to print character
pd.print("Hello C++");
return 0;
}
それを試してみてください
上記のコードをコンパイルして実行すると、次の結果が生成されます。
Printing int: 5
Printing float: 500.263
Printing character: Hello C++
C++ での演算子のオーバーロード
C++ の組み込み演算子のほとんどを再定義またはオーバーロードできます。このようにして、カスタム型に演算子を使用できます。
オーバーロードされた演算子は、キーワード operator の後にオーバーロードされる演算子の記号が続く特別な名前を持つ関数です。他の関数と同様に、オーバーロードされた演算子には戻り値の型とパラメーター リストがあります。
Box operator+(const Box&);
加算演算子を宣言して、2 つの Box オブジェクトを加算し、最終的な Box オブジェクトを返します。ほとんどのオーバーロードされた演算子は、通常の非メンバー関数またはクラス メンバー関数として定義できます。上記の関数をクラスの非メンバー関数として定義する場合、次のように、各操作に対して 2 つのパラメーターを渡す必要があります。
Box operator+(const Box&, const Box&);
次の例は、メンバー関数を使用した演算子のオーバーロードの概念を示しています。ここでは、オブジェクトがパラメーターとして渡され、 次のようにthis演算子を使用してオブジェクトのプロパティに アクセスします。
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
上記のコードをコンパイルして実行すると、次の結果が生成されます。
Box1 の容量 : 210 Box2のボリューム: 1560 Box3のボリューム: 5400
オーバーロード可能な演算子/オーバーロード不可能な演算子
以下は、オーバーロードできる演算子のリストです。
+ | - | * | / | % | ^ |
& | | | | 〜 | ! | 、 | = |
< | > | <= | >= | ++ | -- |
<< | >> | == | != | && | || |
+= | -= | /= | %= | ^= | &= |
|= | *= | <<= | >>= | [] | () |
-> | ->* | 新しい | 新しい [] | 消去 | 消去 [] |
以下は、オーバーロードできない演算子のリストです。
:: | .* | . | ?: |
演算子のオーバーロードの例
オーバーロードの概念をよりよく理解するために、さまざまな演算子のオーバーロードの例を以下に示します。
シリアルナンバー | オペレーターとインスタンス |
---|---|
1 | 単項演算子のオーバーロード |
2 | 二項演算子のオーバーロード |
3 | 関係演算子のオーバーロード |
4 | 入出力演算子のオーバーロード |
5 | ++ および -- 演算子のオーバーロード |
6 | 代入演算子のオーバーロード |
7 | 関数呼び出し operator() のオーバーロード |
8 | 添え字演算子 [] オーバーロード |
9 | クラス メンバー アクセス演算子 -> オーバーロード |
C++ ポリモーフィズム
C++ ポリモーフィズム
ポリモーフィズムとは文字通り、複数のフォームを意味します。ポリモーフィズムは、クラス間に階層があり、クラスが継承によって関連付けられている場合に使用されます。
C++ ポリモーフィズムとは、メンバー関数が呼び出されると、関数を呼び出すオブジェクトの型に応じて異なる関数が実行されることを意味します。
次の例では、基本クラス Shape が次のように 2 つのクラスに派生します。
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函数
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存储矩形的地址
shape = &rec;
// 调用矩形的求面积函数 area
shape->area();
// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数 area
shape->area();
return 0;
}
上記のコードをコンパイルして実行すると、次の結果が生成されます。
親クラス領域: 親クラス領域:
間違った出力の理由は、呼び出し元の関数 area() がコンパイラによって基本クラスのバージョンに設定されているためです。これは静的ポリモーフィズムまたは静的リンクと呼ばれます 。関数呼び出しはプログラムの実行前に準備されます。プログラムのコンパイル中に area() 関数が設定されるため、これはアーリー バインディングと呼ばれることもあります。
しかし今のところ、プログラムを少し変更して、次のように、Shape クラスの area() の宣言の前に キーワードvirtualを配置しましょう。
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
変更後、前のサンプル コードをコンパイルして実行すると、次の結果が生成されます。
長方形クラスエリア 三角クラスエリア
この時点で、コンパイラはポインターの型ではなく、その内容を調べています。したがって、クラスtriとrecのオブジェクトのアドレスは*shapeに格納されるため、それぞれのarea()関数が呼び出されます。
ご覧のとおり、各サブクラスには関数 area() の独立した実装があります。これが、ポリモーフィズムが一般的に使用される方法です。ポリモーフィズムを使用すると、いくつかの異なるクラスを持つことができます。それらはすべて、同じ名前で異なる実装の関数を持ち、関数のパラメーターは同じにすることさえできます。
仮想関数
仮想関数は、 キーワード virtualを使用して基本クラスで 宣言された関数です。基底クラスで定義された仮想関数を派生クラスで再定義するときは、その関数に静的にリンクしないようにコンパイラに指示します。
プログラムの任意の時点で、呼び出されるオブジェクトのタイプに基づいて呼び出す関数を選択できるようにする必要があります. この操作は、動的リンクまたは遅延バインディングと呼ばれます.
純粋仮想関数
派生クラスでの関数の再定義がオブジェクトにより適したものになるように、基底クラスで仮想関数を定義したい場合がありますが、基底クラスで仮想関数の意味のある実装を提供することはできません。純粋な仮想関数を使用してください。
基本クラスの仮想関数 area() を次のように書き換えることができます。
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
= 0 は、関数に本体がなく、上記の仮想関数が純粋な仮想であることをコンパイラに伝えます。