• ベストアンサー

代入の方法(C++)

Fookyの回答

  • ベストアンサー
  • Fooky
  • ベストアンサー率71% (59/82)
回答No.2

関数の中で定義されている変数(インスタンス)の 寿命を把握していれば、状況に合わせて適切なコード が書けるんじゃないでしょうか。 T& f(...)に対して、T a = f(...); または、 T& T::f(...)に対して、T a = v.f(...)も 当然ありです。 ただ、その際に問題となるのは返り値の寿命です。 返り値のインスタンスが、f(...)の中のローカル変数であっては いけない訳です。理由はお分かりと思いますが、ローカル変数は その変数の実行が終了した時点で解体されますので、参照先の アドレス上のインスタンスを指すリファレンスは、 解体後には使えないからです。ですから、 T& f1(...){ T ret; ~~~: return(ret); } ~~~ T a = f1(...); は、不可(動作不定)です。 では、どのような場合にリファレンス返しがOKなのか、 ということですが、最も代表的な例が、 T& T::f2(...){ ~~~; return(*this); } ~~~ T v; ~~~ T a = v.f2(...); です。自分自身へのリファレンスを返すメンバを定義して、 それを代入するわけです。こうすれば、リファレンスの指す オブジェクト(v)の寿命は、T v;が実行された関数内(グローバル に定義されたなら常に存在)であるので、同一関数内なら、 T a = v.f2(...);がOKになります。 その他の方法としては、あまりエレガントでありませんが、 T& f3(...){ static T ret; ~~~; return(T); } T& T::f4(...){ static T ret; ~~~; return(T); } という方法もあるでしょう。ただし、これは、マルチスレッド化した ときに問題が起こります。(retに関して相互排除しなければならない) あと、やってはいけないのは、 T& f5(...){ T *retp = new T; ~~~; return(*retp); } または、 T& T::f6(...){ T *retp = new T; ~~~; return(*retp); } です。これは、staticと同様、関数内のローカル変数の寿命の 問題をクリアしていますが、メモリリークが発生し易いコードです。 特に、 T a = f5(...); T a = v.f6(...); という文脈で使用すると、ほぼ100%(余程手の込んだコードを書かない 限り)、メモリリークが発生します。 なぜなら、f5やf6の中で割り当てたメモリを参照するポインタまたは リファレンスが、T;;operator=(const T&)の終了以降に、存在しなく なるため、newと必ず対になるべきdeleteを実行できなくなるからです。 とにかく、関数内で定義されたローカル変数は、関数終了時に 解体されるので、それを指すリファレンスを返すことはできない。 リファレンス返しをしたければ、 ・*thisへのリファレンスを返すメンバ関数 ・返り値用の静的変数を関数内で定義し、  それへのリファレンスを返すメンバ関数 などによって行う、ということになると思います。 また、ここからは回答でなくアドバイスですが、 上記の例のクラスTが余程大規模であるか、 または、代入演算が余程頻繁に行われるかしない限り、 インスタンス返しによるオーバーヘッドは、 気にするほど大きなものにはならないと思います。 もし、あるクラスTに対して、インスタンス返しの実行時間が、 リファレンス返しの1.5倍かかったとしても、その代入計算が 行われる時間が、プログラムの全実行時間の1%しかないので あれば、0.5%の違いしか生じないわけです。(全実行時間の 1%というのは相当大きく見積もった例です) ですから、インスタンス返しの方が見た目分かり易いですし、 実は時間もそんなに変わらないんだとしたら、 無理やりにリファレンス返しをする理由もないかも知れません ので、その辺の理性的な検討もされては如何でしょう。

motsuan
質問者

お礼

いろいろなパターンを示していただき 回答とアドバイスどうもありがとうございます。 とくに寿命でトラブルの起きやすい部分を きっちり説明していただき、ちょっとすっきりしました。 私なりには T& T::f2(...){ ~~~; return(*this); } のような関数を用意して T::f2(...)の中で自身を書きかえるようにして 他のインスタンスに代入する場合は T a; T v = a; v.f2(...); とやるのがいいのかな、と思うようになりました。 本当にどうもありがとうございました。

関連するQ&A

  • C言語(引数)

    はじめまして。 C言語を習い始めたものです。 関数を定義するとき、よく耳にする、仮引数や実引数があると思います。 仮引数は関数の定義内で値をうけとる変数のことであり 実引数は関数を呼び出す際に渡す値を実引数というらしいのですが どこからどこまでを仮引数と呼ぶのかわかりません。 例えば、 fの関数の定義内で ↓があるとします。 (関数にする意味はないのですが、確認のためあしからず・・・) double f(double x) {     x=5;     return(x); } この場合、仮引数とよばれるものは double f(double x)の xが仮引数であって x=5;のxは仮引数と呼ばないのでしょうか?? もしそうならば void f(double x) { printf("%f",x); } のprintf("%f",x);内のxは仮引数とよぶのでしょうか? 質問の内容が意味不明かもしれませんが よろしくお願いします。

  • 関数に値の代入 [C言語]

    C言語初心者です。関数とポインタについて勉強していたのですが、ふと関数の型を知りたくなってVC++で型を調べてみたんです。そしたら、void型で引数のない関数の型は void (__cdecl*)() となっていました。voidと__cdeclはわかります。 そしてこれ型に*が入ってるじゃないですか。ということは関数はポインタということになると思います。なので私はもしかしたら値の代入ができるのではないか、と思ったのです。早速、 f1=f2;(f1とf2は型と内容の同じ関数) や、 (*f1)=(*f2); としてポインタの中身や参照先の関数の実態の値を処理中に書き換えてみようとしました。ですが、多分そうなるとは思ったのですが、コンパイルエラーが出ました。 《エラーの内容》 error C2106:'=':左のオペランドが、左辺値になっていません。 warning C4550:式は引数リストのない関数として評価します。 関数を書き換えようとすること自体馬鹿げていることは重々承知です。でも、微かにいけそうな気がするんですよ。代入させたくないなら関数の型は一律constにすると思うし(実際関数をconstをつけて宣言してもOKだった、(プロトタイプ有り無しでも))、関数への代入は問答無用で駄目なのならばそういうエラーメッセージを出すと思うんです。 関数の書き換えは100%無理でしょうか?それとも関数を書き換える方法があるでしょうか?回答よろしくお願いします。

  • 警告「代入される前に使われている」を出す方法

    bccで以下のコードをコンパイルすると。 「'i' は、おそらく値が代入される前に使われている」 と警告が出ますが、 「'h' は、おそらく値が代入される前に使われている」 とは警告を出してくれません。 警告を出す方法ってないでしょうか? Hogeメンバに bool 型の初期化フラグでも実装しようかな・・・。 #include <iostream> class Hoge { public: Hoge(){ } Hoge( int i ){ t = i; } operator int(){ return t; } int t; }; int main() { int i; Hoge h; std::cout << i << std::endl; std::cout << h << std::endl; return 0; }

  • c言語の関数定義について

    次の関数定義を考える. int f(int x) {if (x > 0) {return x * f(x-1);} else {return 1;} } この関数f と働き(すなわち,引数と戻り値の関係)が同じで再帰呼出(recursive call) を使わない関数g をC で定義せよ.ただし,オーバーフロー(overflow) については考慮しなくてよい. ”この関数f と働き(すなわち,引数と戻り値の関係)が同じで再帰呼出(recursive call) を使わない関数g をC で定義せよ”って理解できません、どのように定義したいいか、ご教授お願いします。

  • 【C++】関数ポインタの使い方

    関数ポインタの使い方で悩んでいます。 下記の (1)のようにグローバルメソッドとして定義したメソッドを関数ポインタに代入することは出来るのですが、 (2)のようにクラスのメンバメソッドとして定義したメソッドは関数ポインタに代入することは出来ませんでした。 Error:バインドされた関数へのポインターは関数の呼び出しにのみ使用できます。 というエラーが発生します。 関数ポインタに外部参照でメソッドを代入することは出来ないのでしょうか? -----(1)------------------------------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; int f(int a, int b){ return a * b; } int _tmain(int argc, _TCHAR* argv[]) { typedef int (* FUNC_POINTER)(int, int); FUNC_POINTER fp; fp = f; cout << fp(1,2) <<endl; getchar(); return 0; } ------------------------------------------------------------------------- -----(2)------------------------------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; class MPointerList{ public: int f(int a, int b){ return a * b; } }; int _tmain(int argc, _TCHAR* argv[]) { typedef int (* FUNC_POINTER)(int, int); FUNC_POINTER fp; //fp = f; MPointerList mP; fp = mP.f; cout << fp(1,2) <<endl; getchar(); return 0; } -------------------------------------------------------------------------

  • C++ template operator T()

    ソースを見ていて分からないところがあったので教えて下さい。 なんと説明すればいいのか分からないので下に要約したソースを書きます。 template <typename T> class Hoge { T a_; public: Hoge(T a = 0) : a_(a) {} operator T() const { // ココが分からない return a_; } }; 最初は、関数オブジェクトかと思ったのですがそうではないですよね? operator()(引数) ですよね? 次は、Tのコンストラクタかと思ったのですが、class Hoge の中に書くのも変な気がしました。

  • 引数について質問

    私プログラミング初心者ですので、できるだけ優しい解説をしていただければ幸いです! 引数について、以下のような解説がありました。 「引数には仮引数と実引数の2種類が存在する。仮引数は、関数を定義する際に変数で指定する引数である。また、実引数は、プログラムの実行時に関数に引き渡される値となる引数である。つまり、関数の実行時には、実引数の値が仮引数に代入されることになる。」 質問:1「関数を定義する際に変数で指定する引数である。」という記述の中で「関数を定義」とありますが、実際のソースコードにおいて何に対応するかわかりません。簡潔なソースコードを交えて解説していただければ幸いです。 質問2:「関数を定義」に限らず、プログラミングにおいて「定義」という言葉をよく見ますが、これは本質的にどういう意味をもっているのでしょうか?具体的なソースコースコードを交えて解説してくださると幸いです。 もしかして、その定義とは例えば「public static void main(String arg[]){」のような「メソッド宣言」のことですか? 質問3:「関数の実行時には、実引数の値が仮引数に代入されることになる。」と書いてありますが、 これはどういうことですか、僕が実際にソースコードで記述してみるので、その考えが正しいか判定してください package 第4章; public class A { public static void main(String arg[]){ double x; x=Math.sqrt(2.0); System.out.println("2.0の平方根は"+x); } } 僕の考え:String arg[]が仮引数で、実引数2.0がString arg[]に代入されるってことでしょうか? 「定義」といえば、上記のソースコードでは、public static void main(String arg[]){ 以外見当たらないので、、 僕の考え2:Mathクラスは、標準クラス(javaが最初から備えているクラス)だから、プログラマが「関数を定義」しなくても予め関数が定義されているから、関数を定義する必要がない、ということでしょうか?

    • ベストアンサー
    • Java
  • C++で継承元のクラスの代入演算子を呼び出す方法

    C++であるクラス継承したクラスの代入演算子で 継承元のクラスの代入演算子を呼び出す方法がわかりません。 こんな感じのソースです。 class T { T& operator=(const T &t) { } } class U :: T{ U& operator=(const U &u) { // ここでT.operator=()の代入演算を実行したい } } ((T)(*this)).operator=((T)(u))のようにキャストすればいけるかと思ったんですが、 コンパイルエラーでした。 よろしくお願いします。 また、これはプログラム上好ましくない手法でしたら、 別な実現方法をお教えください。

  • C言語 exitで終了した関数の戻り値

    引数が負の整数であればエラーを表示し終了、0以上の整数であればそのまま戻り値とする関数 int example(int a) { if( a < 0 ) { printf("Error!\n");   exit(1); } return(a); } があったとします。 ここでたとえば  int s1,s2; に対して、 s1 = example(3); とすれば、 s1 = 3 となりますが、 s2 = example(-5); とすれば、 s2 には何が代入されているのでしょうか? 例に書いた関数はしょーもないものですが、 もう少し難しい関数を使って変数に値を入れて、その値で後でfor文などを使って仕分けていくようにしたいのですが、エラー時に代入されるものがわからなくて困っています。 エラー時のみに目印となる値、たとえば -1 などを戻り値とすることはできません。 おねがいします。

  • 合成積の式にフーリエ変換の関数を代入可能ですか?

    (f*g)(t) = ∫[-∞,∞] f(s) g(t-s) ds のf(s)とg(t-s)の部分にフーリエ変換の関数F(s)とG(k-s)を代入できますか? 定義を二つ書きます: ・フーリエ変換の式 F(k) = ∫[∞,-∞] f(t) exp^(-ikt) dt (式5.26) 関数F(k)は非周期関数f(t)のフーリエ変換と呼ばれ、(式5.26)はフーリエ変換を計算する式である。 ・合成積 区分的に滑らかで絶対可積分である2つの関数f(t), g(t)が与えられたとき、f(t)とg(t)の合成積(または、たたみこみ)を (f*g)(t) = ∫[∞,-∞] f(s) g(t-s) ds (式6.28) によって定義する。この式の左辺では、f*gが1つの関数の名前であることをはっきり示すために括弧で括ってあり、合成積はtの関数なので(t)と書いてある。 ・・・上記二つの式を踏まえて、 (F*G)(t) = ∫[∞,-∞] F(s) G(k-s) ds (式6.28)' と代入できますか?