• ベストアンサー

質問が多いです・・・・

C++のconst+その他について疑問が出てきてしまったため質問しようと思います。 現在下記のURLで示したサイトで勉強中です。http://www5c.biglobe.ne.jp/~ecb/cpp/04_19_02.html :::質問1::: 上記のサイトで提示してあるプログラムについて質問をしていきますが、上のプログラムを全て理解しなくても大丈夫だと思います。質問内容は表記についてです。 プログラムではたくさんの代入演算子、比較演算子を自分で定義していますが、比較演算子についてクラス内部で定義してあるものと、外部関数をfriend登録してある関数とに分かれています。この差というのは起動オブジェクトがそれ自身のクラスから生成された場合はメンバ関数として定義し、起動オブジェクトが単なる変数などの場合外部関数として定義するという区別でいいのでしょうか? :::質問2::: 質問1で紹介したたくさんの比較演算子を自分で定義してある部分についてですが例えば次のような場合です。 bool String::operator< (const char* p)const{ return strcmp( pStr, p ) < 0; } この場合のconstについて質問したいところがあります。boolの行の一番最後のconst・・・・ この意味はoperatorの前に書いてあるクラスのメンバ変数をこの関数の処理で変更しないという解釈でよいのでしょうか? そしてreturn文についてです。 この関数がboolと定義されているところから想像はつきますが、 strcmp( pStr, p ) < 0 この結果が返されるということでいいんですよね?このような記述の仕方は初めてみましたが、例えば if( strcmp( pStr, p ) < 0 )   return TRUE; else   return FALSE; と記述しても同じですよね? :::質問3::: キャスト演算子の部分で次のように記述してあります。 String::operator const char*()const{    return pStr; } 恥ずかしながら、どのような処理になっているかさっぱりです。もし分かる方がいたら解説をお願いしたいです。 長く質問してしまって申し訳ないです。 全てといわずこのどれか1つでもいいので、答えることができる方はよろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • MrBan
  • ベストアンサー率53% (331/615)
回答No.5

> 左辺にクラスを持ってくるためというのが分かりません。左辺は定数ですよね? ごめんなさい、右側、ですね…orz operator <(const char*p)だけだと、(str < p)はかけても(p < str)は書けないってことです。 外部定義の二項オペレータで(str < p)用や(p < str)用を定義することはできますが、 クラスのメンバで定義すると、自分自身は左側と解釈されるので。 # ちなみに、演算子の解釈は決まっているので、現実的にはコンパイルエラーなどが想定可能です。 friendをつけているのは、既にあるようにメンバを直接参照させるためですね。 getFoo()とか内部で使ったりすれば、必ずしも必須ではありませんが、 一般的にはfriendの使いどころかなとは思います。 > しかしなぜ本文ではconstをつけ、いちいちオブジェクトを作成してその値を渡しているのでしょう? 演算子からプログラマが期待する動作に反してるから、やらないほうがいい。 着たい動作にはできるだけ従っておけ、ということだと思います。 String str; str + "foo"; // これでstrの内容が"foo"になってたら(というか変更されたら)プログラマが混乱します。 str += "foo"; // こう書くのは自然。 str = str + "foo"; // これもあり。 演算子をオーバロードする場合の戻り値とかにはある程度お約束があります。 基本型と同じシンタックスにするためのもので、特になにもなければ普通はこれを踏襲します。 > 戻り値にもconstがかかるのですね。勉強になります。 例が混乱させてる気がしますが、そういうことではないです。constがかかっているのは、thisの方です。 thisがconstになっているので、Foo&への変換でconst_castしないといけないとエラーになるのです。 関数がconstでないと、thisもconstでないので、Foo&に暗黙で変換できるだけ。 thisを使ってなければ戻り値自体はFoo& foo()constもできますし、 戻り値位外でもthisを非constにしようとすれば同様のエラーになります。 constについては、なくても動作自体はする場合も多いのですが、 これがないがゆえに使う側にconst_castが必要になったりすることも多々あり… 可能な限りつけておくのがC++の作法かと思います。

noconan
質問者

お礼

返答ありがとうございました。 >外部定義の二項オペレータで(str < p)用や(p < str)用を定義するこ >とはできますが、 >クラスのメンバで定義すると、自分自身は左側と解釈されるので。 やはり間違いでしたか。この説明をきいて自分の考えが当たっていたことに気づけてよかったです。(str < p)この場合も外部定義できるが、メンバで定義すれば自分自身左側と解釈するため、左側に来る関数は全て内部で定義したということですね。 とても分かりやすかったです! 次に演算子についてですが・・・ 自分はアホでしたね・・・・・・ 上の例で一目瞭然ですが、str + "foo"; このように記述してstrが更新されるなんてことは誰一人として考えませんよね・・・・・ これもまた明確な答えで分かりやすかったです。 最後にconstについて。 これも自分の勘違いでした。thisがconstになっている・・・つまり自分自身が const 担っているということで、それが返されるわけだから、返されたほうも当然constでなくてはなりませんよね。 質問が解決できとても感謝しています。

その他の回答 (5)

  • ddnp009
  • ベストアンサー率25% (15/58)
回答No.6

#3です。 >この関数の起動オブジェクトは const char*()だと思うのですが、 >char*の後ろの()は何を意味しているのでしょう? 当該クラスを別の型として表現することを、ユーザー(プログラマ)自身で定義する。 カッコは・・・そこに存在することに意味があるので、中には何も入りません。 それとすみません。 メンバ関数末尾のconst、これのサンプルが良くなかったです。 当該関数内では、そのクラス自身がconst扱い(当然メンバも)。 thisポインタは、const this。 なので、戻り値の型 String& と一致させることが出来ない、ということです。

noconan
質問者

お礼

返答ありがとうございました。 >メンバ関数末尾のconst、これのサンプルが良くなかったです。 いえいえ、自分の力不足でした。 No5さんの返答と同時に読むとより理解が深まります。 おかげでconstを見たときに、その時点で理解できそうです。 ありがとうございました。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.4

> このプログラムを次のように書き換えれば同じことができないでしょうか? > > void operator+(const char* p); > > void String::operator+(const char* p){ >   strcat(pStr, p); > } まずこの演算子は、Stringクラスの変数 + いわゆるC形式の文字列 の演算を行うものですから、戻り値の型を void にしてしまっては 連結した文字列の持って行き場がありません。 上記の書き方はむしろ += 演算子ですね。 その場合でも戻り値の型が void だと通常の整数なんかのときの振る舞いと変わってしまいます。 もちろんわかった上であえてvoidにすることはあります。 そして大きな問題があります。 >   strcat(pStr, p); さて、このpStrが指している領域はどのくらいの大きさでしょうか? pが指している文字列を追加したりするとオーバーフローしませんか? > String String::operator+(const char* p)const{ >  String str(*this); >  str+=p; >  return str; > } これだと実際の連結は str+=p; でやるわけですが、ここでstrはStringクラスの インスタンスですので、operator += が使われます。 この演算子の定義を良く見てほしいのですが、自分自身の長さと連結対象の 文字列の長さの合計を計算してそれにみあった新しいメモリを確保しています。 最後になぜ operator + が constなのかですが、 opetator +を呼び出す元となったインスタンスは変更されてませんよね? Cstring x = s.opetator+(p) といった形で使うわけでけど、sの内容とpの内容を連結した新しいオブジェクトを 作っていて、sそのものの中身は何も変わっていません。 ですので const なわけです。 ついでに#3のほうの質問にも。 > この関数の起動オブジェクトは const char*()だと思うのですが、char*の後ろの()は何を意味しているのでしょう? 関数だということをあらわすカッコですよ。 operator char*は引数をとらないので(実際には自分自身が隠れ引数として渡されてますが)、 カッコの中身が空になっているだけです。 Csting s; const char *p; p = s; とあったときに、p = s.operator const char*(); という解釈がされます。引数がなくても関数なのでカッコが必要というわけです。 C++マニアック,オペレータのオーバーロード,operator overload http://homepage2.nifty.com/well/Operator.html この辺なんか参考になるかも。

noconan
質問者

お礼

返答ありがとうございました。 長々と説明をしていただき、とても感謝しています。 sakusaker7さんがおっしゃられるように、何も対策をしないと軽々オーバーフローになりますね・・・・ 確かに自分の記述したプログラムではバグだらけですw 次の質問のconstの理由も分かりやすく説明してくれたため、理解することができました。 そして最後の回答ですが、照会してくださったページを見たら一発で理解できました。 うやむやにしてきたものが解決できほっとしています!

  • ddnp009
  • ベストアンサー率25% (15/58)
回答No.3

質問2, 3について。 まず2。 >この意味はoperatorの前に書いてあるクラスのメンバ変数をこの関数の処理で変更しないという解釈でよいのでしょうか? あたり。でもそれだけじゃない。 戻り値についても、constを返すことになる。 乱暴に言うと、関数全体をconst扱い。 なので、もし仮に代入演算子 String& String::operator=(const char* src) const // ←もしここ(末尾)にconstがあれば・・・ {   /* いろいろ */   return *this; // ←これが通らない( const String& しか返せない) } >この関数がboolと定義されているところから想像はつきますが、 >strcmp( pStr, p ) < 0 この結果が返されるということでいいんですよね? >このような記述の仕方は初めてみましたが、例えば >if( strcmp( pStr, p ) < 0 ) >  return TRUE; >else >  return FALSE; >と記述しても同じですよね? TRUE, FALSEが それぞれ true, falseならあたり。 ただし冗長。 /* int a = 1; */ if ( a == 1 ) {   return 1; } なんて書かないでしょ。この int が bool になっただけ。 "strcmp( pStr, p ) < 0" ← この式で bool の値を持ちます。 質問3 >String::operator const char*()const{ >   return pStr; >} 型変換。 これがあれば、const char* を使うところで Stringオブジェクトを渡せる。

noconan
質問者

お礼

返答ありがとうございました。 質問2の回答に関して。 戻り値にもconstがかかるのですね。勉強になります。 例えば質問3で >String::operator const char*()const{ >   return pStr; >} このようになっている場合は戻り値が代入される変数もconstでなくてはならないのですね。 それにTRUE、FALSEを間違えて記述してしまい申し訳ありませんでした。自分でもtrue、falseと分かっていたのですが、この質問を書く直前にSDKでプログラムを書いていたため、癖でこちらにも書いてしまいました。申し訳ありません。 そして確かに提示されたプログラムのようなものは冗長ですね。初心者には自分で提示したプログラムの方がしっくりくるかもしれません。冗長ですがw できるだけ自分もコードが短くなるように心がけて生きたいと思います。 質問3にたいして。 ありがとうございました。おかげで内容を理解することができましたが、一部気になることがあります。 この関数の起動オブジェクトは const char*()だと思うのですが、char*の後ろの()は何を意味しているのでしょう?最初このカッコのなかに引数が入るのでは?と思っていたのですが違ったようです。代入演算子もないですし・・・・・直感的にこの関数は理解できないのですが、どのように解釈すればよいでしょう?

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

質問1についてだけ。 Stringオブジェクトsと、文字列(というかchar *というか)pがあったときに、 s == p と書くと、Stringクラスのメソッド // 比較演算子 bool String::operator==(const char* p)const{ return strcmp( pStr, p ) == 0; } これが呼ばれます(s::operator==(p))。 しかし逆に、 p == s と書いてしまうと、コンパイラは 上記のような解釈の手続きを持っていませんので、 s が想定外の解釈をされて結果がどうなるかわからなくなってしまいます。 そこで、char*, String という引数を取る関数を定義しているのですが、 その関数の中で Stringの実装を覗き見しているので、 firend 宣言しておく必要があります。 これによって、pの指す文字列と Stringの抱えている実体とを strcmpで比較することができるというわけです。

noconan
質問者

お礼

返答ありがとうございました。 解説によると、やはり起動オブジェクトがクラスでない場合外部関数と定義していますよね。 そしてその外部関数はStringオブジェクトのprivateで宣言されたメンバ変数にアクセスするため、friend登録しているわけですね。 ありがとうございました。一応整理できたと思います。まだ自分で作っていくのは大変そうですが・・・・ ついでに申し訳ないのですが、もう1つ質問しても良いでしょうか? プログラム中に以下の定義があります。 String operator+(const char* p)const; String String::operator+(const char* p)const{  String str(*this);  str+=p;  return str; } このプログラムを次のように書き換えれば同じことができないでしょうか? void operator+(const char* p); void String::operator+(const char* p){   strcat(pStr, p); } 凄く単純に書いてしまいましたが、このように記述しても動作すると思います。しかしなぜ本文ではconstをつけ、いちいちオブジェクトを作成してその値を渡しているのでしょう?

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.1

:::質問1::: その例では、左辺にクラスを持ってくるためにfriendにしてます。 メンバで定義すると右辺に解釈される(引数が左辺になる)ので。 # 厳密にはルックアップの評価順も違ったかな(メンバとfriend) :::質問2::: この関数の処理で変更しないという解釈でよいのでしょうか? Yes strcmp( pStr, p ) < 0 この結果が返されるということでいいんですよね? Yes  このような記述の仕方は初めてみましたが、例えば if( strcmp( pStr, p ) < 0 )   return TRUE; else   return FALSE; No。 trueとTRUE、falseとFALSEは違う。 前者(小文字)はC++の予約語。後者(大文字)は言語標準には存在しない(Windows等で独自に定義してることもある) そして、boolに格納されるべきは、trueとfalseである。(Windowsの BOOLならTRUE/FALSEでもよいが) それ以外の考え方は間違ってはいない。 :::質問3::: 例えば String str; const char* s = str; // ここで当該演算子が呼ばれる と書くと、sにstr.pStrの値が格納される。

noconan
質問者

お礼

返答ありがとうございました。 質問2の回答は間違えていて申し訳ありません。この勉強と同時にSDKの勉強もしているためfalseをFALSEと書き間違えることが時々・・・・ 質問1の回答で分からないことが・・・ >その例では、左辺にクラスを持ってくるためにfriendにしてます。 >メンバで定義すると右辺に解釈される(引数が左辺になる)ので。 例えば下のような例。 friend bool operator< (const char* p, const String& s); Stringのオブジェクトをstrとするとき これは if( p < str ) このようなときに上のプログラムが呼ばれると思うのですが、左辺にクラスを持ってくるためというのが分かりません。左辺は定数ですよね? 自分の力不足が否めませんが、詳しく解説をお願いできないでしょうか?