←検索窓の楽しみ方
  ショッピングモール  掲示板ランキング


掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板1

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.4645

自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---初心者(2005/09/25 22:42:52)


現在,ハンドルクラスを勉強中です.

例えば,(標準ライブラリのcomplexでなく)自作で複素数クラスをハンドルクラスで作った場合,加算演算子をどのように実現すべきかを
教えて頂ければ幸いです.

ハンドルクラス名をComplex,そのメンバをComplex_rep *repとしたとき,
[選択肢1]
<PRE>
Complex Complex::operator+(const Complex &rhs){
return rep->operator+(lhs.rep);
}
</PRE>
とすると,1.0+Complex(2.0, 3.0)のようにdouble型が第一オペランドになる場合を別途に対処しなければならないと思われます.

[選択肢1.5]
Complexの派生クラスDoubleを作成すれば選択肢1の問題を対処できますが,そこまでしなくてはならないのかと思われます.

[選択肢2]
非メンバ関数
<PRE>
Complex operator+(const Complex &lhs, const Complex &rhs){
return Complex(lhs.rep->re()+rhs.rep->re(), lhs.rep->im()+rhs.rep->im());
}
</PRE>
では,実装を隠蔽するなどの,ハンドルクラスの意義がなくなってしまうと思われます.

同様に,
自作文字列クラスをハンドルクラスで作る際の,
cstringとの連結に加算演算子を使う場合や,
自作多項式クラスをハンドルクラスで作る際の,
double型との加減乗算でも
同じ問題があると思われますが,良く分かっておりません.

何かアドバイスを頂ければ幸いです.


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法 4646 YuO 2005/09/25 23:39:47
<子記事> Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法 4648 かずま 2005/09/26 18:43:39


No.4646

Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---YuO(2005/09/25 23:39:47)


>例えば,(標準ライブラリのcomplexでなく)自作で複素数クラスをハンドルクラスで作った場合,加算演算子をどのように実現すべきかを
>教えて頂ければ幸いです.
(snip)
>[選択肢2]
>非メンバ関数

基本的には選択肢2。
ただし,
>では,実装を隠蔽するなどの,ハンドルクラスの意義がなくなってしまうと思われます.
なのは,全てを加算演算子で行っているため。
例えば,
Complex operator+ (const Complex &lhs, const Compelx &rhs)
{
    return Complex::Add(lhs, rhs);
}
とすれば,実装はちゃんとComplexクラスの中に隠蔽できます。


この投稿にコメントする

削除パスワード

No.4652

自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---初心者(2005/09/26 22:08:40)


お返事有難うございます.

>Complex operator+ (const Complex &lhs, const Compelx &rhs)
>{
>    return Complex::Add(lhs, rhs);
>}

>とすれば,実装はちゃんとComplexクラスの中に隠蔽できます。

凄い!
メンバ関数にこんな呼び出し方があったなんて.
(って物凄く初歩的なのかしら?)
ユーザにComplex::Add(const Complex &, const Complex &)を無闇に使われないためにはprivateにして,
非メンバ関数 operator+(const Complex &, const Complex &)を
friendにすれば良いのでしょうね.

非常に勉強になりました.ありがとうございました.


この投稿にコメントする

削除パスワード

No.4724

const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---初心者(2005/10/10 17:58:48)


>>例えば,(標準ライブラリのcomplexでなく)自作で複素数クラスをハンドルクラスで作った場合,加算演算子をどのように実現すべきかを
>>教えて頂ければ幸いです.
(中略)
>例えば,
>Complex operator+ (const Complex &lhs, const Compelx &rhs)
>{
>    return Complex::Add(lhs, rhs);
>}
>とすれば,実装はちゃんとComplexクラスの中に隠蔽できます。

暫く考えていたのですが,やはり自力では解決できず,
お力をお借りしたく,お願い申し上げます.

(1/2)
確かに,Complex::Addを
const Complex::Add(const Complex &lhs, const Complex &rhs){
  return Complex(*(lhs.Rep)+(*(rhs.Rep)));
}

と実装して,
Complex_repを引数に取るコンストラクタComplex::Complex(const Complex_rep &)を用意すれば,
Complexクラスオブジェクト同士の加算を行うことができますが,
単に,
operator+(const Complex &lhs, const Complex &rhs){
  return Complex(*(lhs.Rep)+(*(rhs.Rep)));
}

としたことと変わってないように思われるのです.
どうなのでしょう?
私の勘違いを正して頂ければ幸いです.

(2/2)
関数Complex::Addはstaticメンバ関数にしないといけないのですよね?
「staticメンバ関数はstaticデータメンバにアクセスするときに使う」と思っておりましたので
少々違和感を感じるのです.
が,staticメンバ関数にしないと,
次のメッセージが表示されてコンパイルに失敗してしまいます:
complex.cxx:57:
cannot call member function
`const Complex Complex::Add(const Complex&, const Complex&)'
without object


この投稿にコメントする

削除パスワード

No.4725

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---RAPT(2005/10/10 22:37:59)


(1)
operator+ の実装 を隠蔽したいということ自体は実現できていると
思うのですが。

コードを書いているあなた自身には両方とも見えているので勘違いをしている
のかもしれませんが、const Complex::Add の実装はクラス内に隠蔽できます。
一方、operator+ は外部に見えるようにグローバルにするか、friendにする
必要があります。

(2)
「staticメンバ関数はstaticデータメンバにアクセスするときに使う」 と思っておりましたので
不充分です。 static関数にする目的の内の一つに「グローバルな関数だけども、クラスに 所属した方が適当と思われる関数」というものがあります。 なので、クラス所属のstatic関数ではなく、グローバル関数にしても大丈夫 なはずです。でもまぁ、クラスに所属させた方が適切だとは思いますが。



この投稿にコメントする

削除パスワード

No.4726

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---初心者(2005/10/10 23:43:48)


早速のお返事有難うございます。

>(1)

せっかく御解説下さったのに、まだ分かってないようです。

>const Complex::Add の実装はクラス内に隠蔽できます。

「クラス内に隠蔽」について教えて頂ければ幸いです。
確かに、operator+(const Complex &, const Complex &)の中にある
Complex::Addは「クラス内に」ありますが、
Complex::Addが何から「隠蔽」されているのかが分かっておりません。

>一方、operator+ は外部に見えるようにグローバルにするか、friendにする必要があります。

当方作成中のComplexクラスのヘッダファイルは次の通りです:
#ifndef __COMPLEX__
#define __COMPLEX__

class Complex_rep;

class Complex{
private:
        Complex_rep *Rep;
        Complex(const Complex_rep &arg);
public:
        Complex(void);
        Complex(double re);
        Complex(double re, double im);
        Complex(const Complex &arg);
        ~Complex(void);
        Complex &operator=(const Complex &rhs);
        const double re(void) const;
        const double im(void) const;
        std::ostream &print(std::ostream &lhs) const;
        static const Complex Add(const Complex &lhs, const Complex &rhs);
        Complex &operator+=(const Complex &rhs);
};

const Complex operator+(const Complex &x, const Complex &y);
std::ostream &operator>>(std::ostream &lhs, const Complex &rhs);

#endif


このままでは、ユーザにComplex::Addを直接使われてしまいます。
かといって、Complex::Addをprivateにすると、
グローバルなoperator+(const Complex &, const Complex &)からも呼び出せなくなってしまいます。
operator+(const Complex &, const Complex &)をfriend宣言すれば、
Complex::Addをprivateにしても、operator+(const Complex &, const Complex &)から呼び出せるので、
この場合はグローバルではなくfrinedにするしかないと思ってよろしいでしょうか?

>(2)
>static関数にする目的の内の一つに「グローバルな関数だけども、クラスに
>所属した方が適当と思われる関数」というものがあります。

解説有難うございます。
なるほど、今回がそのケースということですね。
今回以外にはそのような場合に遭遇していない
(本当は遭遇してるかも知れませんが自覚がない)ので
理解は今一つですが、
今後はそれを踏まえて考えていきたいと思います。


この投稿にコメントする

削除パスワード

No.4728

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---Ban(2005/10/11 02:23:25)


>>const Complex::Add の実装はクラス内に隠蔽できます。
-- snip --
>「クラス内に隠蔽」について教えて頂ければ幸いです。
-- snip --
>Complex::Addが何から「隠蔽」されているのかが分かっておりません。

Add を参照できることと、Add の実装を参照できることの違い、
Add 自体の隠蔽と、Add の実装の隠蔽を区別するべきかと。

>このままでは、ユーザにComplex::Addを直接使われてしまいます。

使われて何がまずいのか良くわかりませんけど、
Add の実装を参照させなければ、Add 自体は参照されてもよいのでは?
# Complex クラスに Add というインターフェイスがあったらまずいですか?

> この場合はグローバルではなくfrinedにするしかないと思ってよろしいでしょうか?
-- snip --
>>static関数にする目的の内の一つに「グローバルな関数だけども、
>>クラスに所属した方が適当と思われる関数」というものがあります。

Add 自体を参照させたくないのであれば、friend にするのはC++のお約束だと思います。
friend はそのクラスのインターフェイスの一部ですから、
隠蔽を強制したいなら friend にするべきだと思います。

# グローバルの operator もインターフェイスの一部だと思ってますし、
# Add でカプセル化してあって、operator は単に使いやすさのものとか、
# 強制の必要が無ければグローバルでもいいと思いますが。


この投稿にコメントする

削除パスワード

No.4733

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---初心者(2005/10/12 04:52:23)


お返事有難うございます.

あいにく私の理解力がないせいで,「なるほど,有難うございます」と一言でお返事でき
ず,大変恐縮ですが,もう少し教えて頂ければ幸いです.

ご説明に現れるキーワードを自分なりに理解し(てみまし)たので,
勘違いを正して頂ければ幸いです.

まずは,自体/実装を参照/隠蔽することを区別するために,
自分の理解を図示してみました:
Addを参照できること ←(対義)→ Add自体の隠蔽
(異義) (異義)
Addの実装を参照できること ←(対義)→ Addの実装の隠蔽

次に,自体の参照と実装の参照を区別するために,
自分の理解を文章化してみました:
Addを参照できること クラスメンバ関数以外でAddを呼び出すことができること.そのためには,Addをpublicにする.
Addの実装を参照できること Addの定義を読むことができること.そのためには,ヘッダファイルにわざわざ定義を書く.普通,そんなことはしない.

さらに,「参照できること」と「隠蔽する」ことが対義であることから,
自体の隠蔽と実装の隠蔽を文章化してみました:
Add自体の隠蔽 (クラスメンバ関数以外で)Addを呼び出せないこと.そのためには,Addをprivateにする.
Addの実装を隠蔽する Addの定義を読んだり書き換えたりできないこと.そのためには,ヘッダファイルとは別のファイルに定義を書き,分割コンパイルしてオブジェクトファイルにする.

この心許ない理解を基に考えると,
Addについては,operator+の実装に用いる目的で導入し始めたのですが,
operator+については,
  • 自体を参照できるようにし(ユーザに使わせるので),かつ,
  • 実装を隠蔽し

たいので,単に,
  • operator+の定義をヘッダファイルとは別のファイルcomplex.cxx内で行うことによっ て,実装を隠蔽し,
  • friend関数にして,定義内でprivateメンバrepを使いつつ,
  • publicにしてユーザが使えるように

すれば,Addを導入しなくとも目的を達成できると考えました.

っとすれば, RAPTさんがNo.4725
「operator+の実装を隠蔽したいということ自体は実現できている」で仰っていたのは,
まさに,「Addなんて導入しなくても,operator+の実装を隠蔽できる」と理解してよろし
いでしょうか?

自分の理解では,
Addを導入せずにoperator+の定義に直接Repを用いる

と結論づけ(てみ)たのですが,
勘違いを正して頂ければ幸いです.


Complex::Addをユーザに直接使わせたくない?


Complex::Addをユーザに直接使われることを問題視したのは,
単に私の思い込み「ユーザに使わせるインタフェースは最小限にする」から
来ているものでしたが,
御解説頂いた何人もの方々がこの問題視に疑念を抱かれたことから,
私の設計指針自体を疑うべきと考えるようになりました.


この投稿にコメントする

削除パスワード

No.4735

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---RAPT(2005/10/12 14:28:45)


Complex::Addをユーザに直接使わせたくない
ということなので、かずまさんのパクリで下記のようなコードを書いてみました。 加算関数 Add の実装を private に置き、アクセサとして、operator+ を public に置く。 operator+ はメンバ関数を呼び出すだけなので実装は隠蔽されている。 と解釈できると思います。 この方法は、YuOさんの static 関数呼び出し方式でもどちらでも使えます。 # 初心者さんの意図を正しく読み取れていなかったらごめんなさい。 // --- main.cpp -------- #include <iostream> #include "Complex.h" int main() { std::cout << 1.0 + Complex(2.0, 3.0) << '\n'; } // --- Complex.h -------- #ifndef _COMPLEX_H_ #define _COMPLEX_H_ #include <iostream> class Complex { class Complex_rep *rep; public: Complex(double re = 0, double im = 0); Complex(const Complex &x); ~Complex(); Complex& operator=(const Complex &x); double re() const; double im() const; friend Complex operator+(const Complex &lhs, const Complex &rhs); friend std::ostream& operator<<(std::ostream& o, const Complex &x); private: Complex& Add(const Complex& other); }; #endif // _COMPLEX_H_ // --- Complex.cpp -------- #include "Complex.h" class Complex_rep { public: double re, im; }; Complex::Complex(double re, double im) { rep = new Complex_rep; rep->re = re; rep->im = im; } Complex::Complex(const Complex &x) { rep = new Complex_rep; rep->re = x.re(); rep->im = x.im(); } Complex& Complex::operator=(const Complex &x) { rep->re = x.re(); rep->im = x.im(); return *this; } Complex::~Complex() { delete rep; } double Complex::re() const { return rep->re; } double Complex::im() const { return rep->im; } Complex& Complex::Add(const Complex& other) { // private this->rep->re += other.re(); this->rep->im += other.im(); return *this; } Complex operator+(const Complex &lhs, const Complex &rhs) { return Complex(lhs).Add(rhs); } std::ostream& operator<<(std::ostream& o, const Complex &x) { return o << '(' << x.re() << ',' << x.im() << ')'; }



この投稿にコメントする

削除パスワード

No.4740

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---初心者(2005/10/13 17:04:23)


>かずまさんのパクリで下記のようなコードを書いてみました。

わざわざソースを示して頂いて有難うございます.
本題以外にも色々と勉強になります.
示して頂いた加算の実装は今まで皆様から示して頂いたものとは異なるものですね.

Addはoperator+=のような働きをするので,
複素数を実装するならばAddをoperator+=に変更してユーザに使わせても良い...っというよりも,
doubleとの類似性からユーザに使わせるようにした方が良いのかも知れませんね.

今までに教えて頂いた複素数クラスの加算の実装について整理させて頂きますと,大きく3種類,Addの使用/不使用を区別すると6種類あることになりました.

1:fromかずまさん
return Complex(lhs.re()+rhs.re(), lhs.im()+rhs.im());
とする.
1':1をAddでラップ
return Complex::Add(lhs, rhs);
として,Addの実装を
return Complex(lhs.re()+rhs.re(), lhs.im()+rhs.im());
とする.
2:書籍からのうろ覚え
return Complex(*(lhs.Rep)+*(rhs.Rep));
とする.ただし,Complex(const Complex_rep &)を実装する.
2':2をAddでラップ
return Complex::Add(lhs, rhs);
として,Addの実装を
return Complex(*(lhs.Rep)+*(rhs.Rep));
とする.ただし,Complex(const Complex_rep &)を実装する.
3:from RAPTさん(オリジナルのAddをoperator+=に改名)
return Complex(lhs)+=rhs;
とする.ただし,Complex::operator+=(const Complex &)を実装する.
3':3をAddでラップ
return Add(lhs, rhs);
として,Addの実装を
return Complex(lhs)+=rhs;
とする.
ただし,Complex::operator+=(const Complex &)を実装する.


いずれも加算の実装が隠蔽されているので,
優劣をつけるならば,実行速度の面から判断することになるでしょうか.
それぞれのバージョンで大量に加算させて時間を測ってみようと思います.


この投稿にコメントする

削除パスワード

No.4741

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---RAPT(2005/10/14 01:24:38)


前のほうは、まだ内容を吟味していないので最後の部分についてだけ。
いずれも加算の実装が隠蔽されているので, 優劣をつけるならば,実行速度の面から判断することになるでしょうか. それぞれのバージョンで大量に加算させて時間を測ってみようと思います.
は違うと思います。処理速度が問題なら、Add関数を隠さない(ラップしない)方が速いと思います。 そうではなく、「要求仕様とクラス設計上の正しさ」を基点にすべきなのでは?



この投稿にコメントする

削除パスワード

No.4743

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---Ban(2005/10/14 01:54:16)


# ながくなってしまいました。ごめんなさい。


operator も、見た目がちょっと変わるだけで、本質的にはただの関数に過ぎません。
仮に、private な operator を作って、friend な Add の中でそれを使ってもいいわけで、operator だろうが、Add だろうが、どっちがどっちでも一緒。
# メソッドの命名は、別の次元で大事なことだとは思いますけど。


また、friend が適しているような operator は、実装上同一クラスのメンバになっていない場合でも、本質的にそのクラスのインターフェイスの一部であることに変わりはありません。
# そのため、普通はクラスと同一の名前空間や、ヘッダ内で宣言します。







そもそも「ハンドルクラス」の話だったと理解しているのですが、この場合重要なのは「二重になっている」ということなはずです。


  • ユーザが使う、外部からのインターフェイス
  • 実装を呼び出す内部クラスとのインターフェイス



operator の式にしろAdd にしろ、外部クラスの実装は、内部クラスのインターフェイスを拘束します。


例えば、外部クラスの実装に、以下のように書いてあれば、
rep->re += other.re();
rep->im += other.im();
内部クラスの rep (やその派生クラス)は、メンバ変数にreとimを持たざるを得ません。(持たないクラスはそのままでは実装になれません)

上記のコードでも、外部に対してはもちろん隠蔽されてますが、将来内部クラスの持ち方などに変更が入ったり、別の内部クラスに変更したり、などの場合には、この部分で外部クラスのオブジェクトファイルは再コンパイルが必須になります。
# 現実にはこんな変更をする可能性は無いのかもしれませんが、
# 例えばrep->member[re]とrep->member[im]に変更するとか。

これは、それだけ内部クラスの実装が、外部クラスに対して隠蔽されていないということを意味しています。
# 逆にこの部分には変更がないことを保証している、とも取れます。>I/F

処理を全て内部クラスのメソッドに丸投げすれば、外部クラスのバイナリは無変更で動作できるかもしれません。
# 実際には生成なども工夫しておかないと変更は難しいでしょうけど。






>いずれも加算の実装が隠蔽されているので,


というわけで、外部に対してはどの実装でも隠蔽されていますが、
内部クラスの実装がどこまで隠蔽されているかは異なると思います。



>優劣をつけるならば,実行速度の面から判断することになるでしょうか.
>それぞれのバージョンで大量に加算させて時間を測ってみようと思います.


よほどクリティカルな処理に使うのでなければ、あくまで可読性や、拡張性などで判断した方がいいと思います。
# もしかしたら特定環境でだけ早ければいいのかも知れませんが、
# 提示されているコード程度だと、最適化次第で結果が変わりそう。

そしてもしも、速度優先で考えて外部のラッパはinline 関数に、とか言い出すと(この是非もここでは別問題)、外部クラスの実装はヘッダに出てくることになったります。そうなったときに、内部クラスの実装がどれだけ隠蔽されているかの意味が出てくることになります。




> Addを導入せずにoperator+の定義に直接Repを用いる
> と結論づけ(てみ)たのですが,
> 勘違いを正して頂ければ幸いです.


「ハンドルとして、完全な内部実装の隠蔽」は難しそうですが、それで要件を満たせて必要十分ならありだと思います。
# 何が一番正しいかは、何が必要かで決まります。




この投稿にコメントする

削除パスワード

No.4729

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---RAPT(2005/10/11 02:23:31)


えー、念のため追記しておきます。

上記の私の回答は、Add と operator+ の実装については追求していません。
この2つを関連づけての回答ではなく、別個の質問に対する回答です。

それを踏まえた上で、今回のクラスの実装について考慮すればいいのでは
ないかと思いますが、いかがでしょうか。

そもそも、operator+ の実装をユーザから隠蔽するために Add() 関数を
実装したい、という話から派生した質問と認識していたのですが、
違っていたらすみません。


このままでは、ユーザにComplex::Addを直接使われてしまいます。
それは問題なのですか? いずれにせよ、その辺の設計については あなたが考えればいいことではないかと思います。 ついでに…、static関数にする目的の内の一つに「普通のコンストラクタ を使わせずに、独自の生成関数を使わせたい場合」ってのもあります。 Foo* m_pFoo; m_pFoo = Foo::CreateNewInstance(this); とか。



この投稿にコメントする

削除パスワード

No.4734

Re:const Complex Complex::Add(const Complex &, const Complex &)の実装
投稿者---初心者(2005/10/12 05:16:37)


お返事有難うございます.

>そもそも、operator+ の実装をユーザから隠蔽するために Add() 関数を
>実装したい、という話から派生した質問と認識していたのですが、
>違っていたらすみません。

いえいえ.
まさに,operator+の実装をユーザから隠蔽したかったんです.
前のRAPTさんのご解説やBanさんからのご解説を踏まえてもう一度考えた結果,
Add関数を導入しなくとも,
  • operator+の実装をヘッダファイルとは別のファイルcomplex.cxxで行い,ユーザにはcomplex.hとcomplex.oとcomplex_rep.oを配布することによって, operator+の実装をユーザから隠蔽し,
  • operator+の定義ではRepを使うのでoperator+をfriendにし,
  • operaot+をpublicにすることでユーザに使えるようにできる.

と理解をしたつもりでいるのですが,
勘違いを正して頂ければ幸いです.


>ついでに…、static関数にする目的の内の一つに「普通のコンストラクタ
>を使わせずに、独自の生成関数を使わせたい場合」ってのもあります。
>
>Foo* m_pFoo;
>m_pFoo = Foo::CreateNewInstance(this);
>
>とか。

これは,仮想コンストラクタとかファクトリー関数とか呼ばれるものに相当するものでしょうか?
っと言っても単に書籍の丸移しですが,
staticメンバ関数に関して書籍を漁ったところ,
確かに,その書籍にはこのような関数については
static宣言する旨が記載されていました.

私には継承はまだまだ先の話(本当はこれがしたくて勉強してるのですが...)のようで,
「なるほど!有難うございます」と一言でお返事できず恐縮ですが,
少なくとも,私の誤解「staticメンバ関数はstaticメンバにアクセスするだけのもの」
を解くには十分に複数の別の例を挙げて頂きまして有難うございます.


この投稿にコメントする

削除パスワード

No.4648

Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---かずま(2005/09/26 18:43:39)


friend を使えばよいのでは?
//--- main.cpp ---
#include <iostream>
#include "Complex.h"

int main()
{
    Complex x(3.14, 2.718);
    Complex z = 1 + x;
    std::cout << z << '\n';
}


//--- Complex.h ---
#ifndef _COMPLEX_H_
#define _COMPLEX_H_

#include <iostream>

class Complex {
    class Complex_rep *rep;
public:
    Complex(double re = 0, double im = 0);
    Complex(const Complex &x);
    ~Complex();
    Complex& operator=(const Complex &x);

    friend Complex operator+(const Complex &a, const Complex &b);
    friend std::ostream& operator<<(std::ostream& o, const Complex &x);
};

#endif // _COMPLEX_H_


//--- Complex.cpp ---
#include "Complex.h"

class Complex_rep {
public:
    double re, im;
};

Complex::Complex(double re, double im)
{
    rep = new Complex_rep;
    rep->re = re;
    rep->im = im;
}

Complex::Complex(const Complex &x)
{
    rep = new Complex_rep;
    rep->re = x.rep->re;
    rep->im = x.rep->im;
}

Complex& Complex::operator=(const Complex &x)
{
    if (&x != this) {
        delete rep;
        rep = new Complex_rep;
        rep->re = x.rep->re;
        rep->im = x.rep->im;
    }
    return *this;
}

Complex::~Complex()
{
    delete rep;
}

Complex operator+(const Complex &a, const Complex &b)
{
    return Complex(a.rep->re + b.rep->re, a.rep->im + b.rep->im);
}

std::ostream& operator<<(std::ostream& o, const Complex &x)
{
    return o << '(' << x.rep->re << ',' << x.rep->im << ')';
}



この投稿にコメントする

削除パスワード

No.4651

Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---かずま(2005/09/26 21:23:51)


> friend を使えばよいのでは?

class Complex は、re() と im() を提供するでしょうから、friend にする
必要はありませんね。とにかく、次のように書けば、Complex.h がインター
フェースであり、Complex.cpp がその実装で、ユーザープログラム main.cpp
には実装が隠蔽されていると言えるのではないでしょうか?
// --- main.cpp --------
#include <iostream>
#include "Complex.h"

int main()
{
    std::cout << 1.0 + Complex(2.0, 3.0) << '\n';
}


// --- Complex.h --------
#ifndef _COMPLEX_H_
#define _COMPLEX_H_

#include <iostream>

class Complex {
    class Complex_rep *rep;
public:
    Complex(double re = 0, double im = 0);
    Complex(const Complex &x);
    ~Complex();
    Complex& operator=(const Complex &x);
    double re() const;
    double im() const;
};

Complex operator+(const Complex &a, const Complex &b);
std::ostream& operator<<(std::ostream& o, const Complex &x);

#endif // _COMPLEX_H_


// --- Complex.cpp --------
#include "Complex.h"

class Complex_rep {
public:
    double re, im;
};

Complex::Complex(double re, double im)
{
    rep = new Complex_rep;
    rep->re = re;
    rep->im = im;
}

Complex::Complex(const Complex &x)
{
    rep = new Complex_rep;
    rep->re = x.rep->re;
    rep->im = x.rep->im;
}

Complex& Complex::operator=(const Complex &x)
{
    if (&x == this) *this;
    rep->re = x.rep->re;
    rep->im = x.rep->im;
    return *this;
}

Complex::~Complex()
{
    delete rep;
}

double Complex::re() const
{
    return rep->re;
}

double Complex::im() const
{
    return rep->im;
}

Complex operator+(const Complex &a, const Complex &b)
{
    return Complex(a.re() + b.re(), a.im() + b.im());
}

std::ostream& operator<<(std::ostream& o, const Complex &x)
{
    return o << '(' << x.re() << ',' << x.im() << ')';
}



この投稿にコメントする

削除パスワード

No.4653

自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---初心者(2005/09/26 22:23:42)


お返事有難うございます.

>次のように書けば、Complex.h がインター
>フェースであり、Complex.cpp がその実装で、ユーザープログラム main.cpp
>には実装が隠蔽されていると言えるのではないでしょうか?

完全なプログラムを掲載して下さいまして,有難うございます.

まだハンドルクラスを良く分かっておらず,
次の私の考えに誤解がありましたら正して頂ければ幸いです.

ハンドルクラスComplexの意義は,Complex.cppファイルでも
実装詳細を表さず,その詳細を全てComplex_repクラスに追いやることにあると記憶しております.
お示しのコードでは,Complex.cppに実装詳細があり,
ComplexクラスとComplex_repクラスをわざわざ両方作成した場合と,
Complexクラス単体で実装詳細も含めて作成した場合とで
違いがないように思われます.
どうなのでしょう?


この投稿にコメントする

削除パスワード

No.4656

Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---かずま(2005/09/27 02:40:46)


> お示しのコードでは,Complex.cppに実装詳細があり,
> ComplexクラスとComplex_repクラスをわざわざ両方作成した場合と,
> Complexクラス単体で実装詳細も含めて作成した場合とで
> 違いがないように思われます.
> どうなのでしょう?

Complexクラス単体の場合、class Complex に double re, im; を記述しな
ければならないので、実装の隠蔽になりません。

Complex_repクラスは必要ですが、それは非公開の Complex.cpp に記述し、
Complex.h だけを公開することで、実装の隠蔽になるのではないでしょうか?


この投稿にコメントする

削除パスワード

No.4659

Re:自作複素数クラスをハンドルクラスで作る場合の加算演算子の実装方法
投稿者---初心者(2005/09/27 03:06:07)


お返事有難うございます。

>> お示しのコードでは,Complex.cppに実装詳細があり,
>> ComplexクラスとComplex_repクラスをわざわざ両方作成した場合と,
>> Complexクラス単体で実装詳細も含めて作成した場合とで
>> 違いがないように思われます.
>> どうなのでしょう?
>
> Complexクラス単体の場合、class Complex に double re, im; を記述しな
> ければならないので、実装の隠蔽になりません。

仰る通りです.
大きな違いがありますね.
#私は何を考えていたのだろう....

> Complex_repクラスは必要ですが、それは非公開の Complex.cpp に記述し、
> Complex.h だけを公開することで、実装の隠蔽になるのではないでしょうか?

確かにそうですね.
ただ,何かoperator+(const Complex &lhs, const Complex &rhs)の実装の際に,
return Complex(lhs.re()+rhs.re(), lhs.re()+rhs.im());
と書いてはマズイんだなぁと思った根拠があったような気がしていたのですが,何の問題もなさそうです.
妙な言いがかり(のつもりはなかったのですが...)を申し上げて大変失礼致しました.
それにもかかわらず,丁寧なお返事を有難うございました.


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity