2017年11月09日
《その111》 フレンド関数(p.502演習14-3)
フレンド関数
演算の対象となる左オペランドが、非クラス型となる利用が想定される2項演算子関数は、クラスのメンバ関数ではなく、非メンバ関数として実現しなければならない。
次のようなクラス C があるとします。
class C {
int a;
int b;
public:
C(int p = 0, int q = 0) : a(p), b(q) { }
int get_a() const { return a; }
int get_b() const { return b; }
};
コンストラクタ C は int型の単一引数で呼び出せますから、変換コンストラクタも兼ねています。
2項+演算子関数を非メンバ関数として実現すると、次のようになります。
C operator+(const C& x, const C& y) {
return C(x.get_a() + y.get_a(), x.get_b() + y.get_b());
}
演算子関数を利用する演算は、次のようなものです。
C x(5, 3);
C y(2);
x = x + y; // ※1
y = 2 + x; // ※2
左オペランドが演算子関数を呼び出すオブジェクトですから、
※1 では C型オブジェクト x が、
※2 では 変換コンストラクタによって 2 が int型から C型に変換された C型オブジェクトが、
それぞれ、演算子関数の呼出し元になります。
ヘッダに置かれた非メンバ関数は外部結合を持ちますから、上記の2項+演算子関数が複数のソースファイルにインクルードされるような使い方をするとエラーになります。
そこで、
inline C operator+(const C& x, const C& y) {
return C(x.get_a() + y.get_a(), x.get_b() + y.get_b());
}
として内部結合を持たせるか、あるいは、クラス定義の中でフレンド関数として
friend C operator+(const C& x, const C& y) {
return C(x.a + y.a, x.b + y.b);
}
のように定義すべきです。クラス定義の中で定義したフレンド関数はインライン関数です。
また、フレンド関数は、クラスの非公開メンバにアクセスできるので、
x.get_a() を
x.a と記述することが可能になります。
新版明解C++入門編 p.502 演習14-3
簡易的な文字列クラス String を作成せよ。以下のコードを参考にして、コンストラクタ・デストラクタ・各メンバ関数を、自分で設計すること。
class String {
int len; // 文字列の長さ
char* ptr; // 文字列の先頭文字へのポインタ
public:
String(const char*); // 変換コンストラクタ
String(const String&); // コピーコンストラクタ
~string(); // デストラクタ
int length(); // 長さを求める
// +, = などの演算子を定義せよ。
};
// String.h
#ifndef ___Class_String
#define ___Class_String
#include <iostream>
class String {
int len;
char* ptr;
public:
String(const char*);
String(const String&);
~String() { delete[] ptr; }
int length() { return len; }
String& operator=(const String& x);
char& operator[](int i);
friend std::ostream& operator<<(std::ostream& s, const String& x);
friend String operator+(const String& x, const String& y);
};
#endif
// String.cpp
#include "String.h"
String::String(const char* s) {
const char* tmp = s;
len = 0;
while (*tmp++)
len++;
ptr = new char[len + 1];
for (int i = 0; i <= len; i++)
ptr[i] = *(s + i);
}
String::String(const String& x) {
if (&x == this) {
len = 1;
ptr = new char[1];
ptr[0] = '\0';
} else {
len = x.len;
ptr = new char[len + 1];
for (int i = 0; i <= len; i++)
ptr[i] = x.ptr[i];
}
}
String& String::operator=(const String& x) {
if (&x != this) {
if (len != x.len) {
delete[] ptr;
len = x.len;
ptr = new char[len + 1];
}
for (int i = 0; i <= len; i++)
ptr[i] = x.ptr[i];
}
return *this;
}
char& String::operator[](int i) {
if (i < 0 || i >= len)
std::cout << "!添字エラー。終了します。\n", exit(1);
return ptr[i];
}
String operator+(const String& x, const String& y) {
String temp = temp;
temp.len = x.len + y.len;
delete[] temp.ptr;
temp.ptr = new char[x.len + y.len + 1];
for (int i = 0; i < x.len; i++)
temp.ptr[i] = x.ptr[i];
for (int i = 0; i <= y.len; i++)
temp.ptr[i + x.len] = y.ptr[i];
return temp;
}
std::ostream& operator<<(std::ostream& s, const String& x) {
return s << x.ptr;
}
// StringTest.cpp
#include <iostream>
#include "String.h"
using namespace std;
int main()
{
String s1 = "how old is ";
String s2 = s1;
String s3 = "she";
String s4 = "?";
s4 = s2 + s3 + s4;
cout << s4 << '\n';
s4[0] = 'H';
cout << s4 << '\n';
cout << s4.length() << '\n';
cout << "I am 100 years old. " + s4 << '\n';
}
--
この記事へのコメント
コメントを書く
この記事へのトラックバックURL
https://fanblogs.jp/tb/6948145
※ブログオーナーが承認したトラックバックのみ表示されます。
この記事へのトラックバック