c++一時オブジェクト

20045 ワード

C++でのオブジェクトの作成は時間と空間の操作がかかることを知っています.必要不可欠なものもありますが、私たちが知らないうちに作成されたオブジェクトもあります.通常、次の3つのケースで一時的なオブジェクトが生成されます.
1、値の方式で関数にパラメータを伝達する;
2、タイプ変換;
3、関数がオブジェクトを返す必要がある場合.
 
この3つの状況を順に見てみましょう.
 
 
一、関数に値でパラメータを渡す.
 
関数にパラメータを渡すには2つの方法があることを知っています.1、値で渡す;2を参照して渡します.値によって伝達する場合、まず関数に伝達する必要があるパラメータを呼び出し、コピー構造関数を呼び出してコピーを作成します.関数内のすべての操作はこのコピーに対して行われます.そのため、関数体でこのコピーを操作しても、元のパラメータには影響しません.次の例を見てみましょう.
  /*-----------------------
Platform:WinXp + VC6
-----------------------*/

#include <stdio.h>
class CTemp
{
public:
    int a;
    int b;
public:
    CTemp(CTemp& t){ printf("Copy function!/n");a = t.a;b = t.b;};
    CTemp(int m = 0,int n = 0);
    virtual ~CTemp(){};
public:
    int GetSum(CTemp ts);
};
CTemp::CTemp(int m , int n)
{
    printf("Construct function!/n");
    a = m;b=n;
    printf("a = %d/n",a);
    printf("b = %d/n",b);
}
int CTemp::GetSum(CTemp ts)
{
    int tmp = ts.a + ts.b;
    ts.a = 1000;           // tm
    
    return tmp;
}
//--------------Main -----------------
void main()
{
    CTemp tm(10,20);
    printf("Sum = %d /n",tm.GetSum(tm));
    printf("tm.a = %d /n",tm.a);
}

--------------------------------------------------------
Output:
Construct function!
a = 10
b = 20
Copy function!
Sum = 30
tm.a = 10
------------------------------------------------------
パラメータとしてGetSumに渡されたとき、tmが
1.コピーコンストラクション関数を呼び出し、GetSum関数内で使用されるコピーを作成します.
2,GetSum関数内でのtmレプリカの修正はtmそのものに影響しなかった.
 
解決策:
1つ目の解決策は、オブジェクト参照を入力することです(参照は元のオブジェクトの別名(Alias)にすぎないことを覚えておいてください).GetSumコードを次のように変更します.
  int CTemp::GetSum(CTemp& ts)
{
    int tmp = ts.a + ts.b;
    ts.a = 1000;     // ts (refer to)
    return tmp;
}

----------------------------------------------------------
Output:
Construct function!
a = 10
b = 20
Sum = 30
tm.a = 1000
--------------------------------------------------------
本を出力することで、定数参照を渡すことで、一時オブジェクトの作成を1回減らすことができます.この変更は小さいかもしれませんが、マルチ継承オブジェクトでは、構築時にすべてのベースクラスのコンストラクション関数を再帰的に呼び出す必要があります.これはパフォーマンスに大きな消費であり、通常、この消費は必要ありません.
 
 
二、タイプ変換生成された一時オブジェクト.
 
タイプ変換を行う場合、変換後のオブジェクトは通常一時的なオブジェクトです.コンパイラはコンパイルによって、私たちが気づきにくい一時オブジェクトを作成します.上記mainコードを再度変更します.
  void main()
{
 CTemp tm(10,20),sum;
 sum = 1000;  // CTemp(int m = 0,int n = 0)
 printf("Sum = %d /n",tm.GetSum(sum));
}

-----------------------------------------------------------
Output:
Construct function!
a = 10
b = 20
Construct function!
a = 0
b = 0
Construct function!
a = 1000
b = 0
Sum = 1000
----------------------------------------------------------
main関数は2つのオブジェクトを作成しますが、出力は3回のコンストラクション関数を呼び出します.これはなぜですか.
キーはsum=1000です.このコード.自体1000とsumタイプは一致しませんが、コンパイラはコンパイルによって1000を参照コンストラクション関数として一時オブジェクトを作成します.
 
解決策:
main関数のコードを少し修正し、sumの申明を「=」番号の前に延期します.
  void main()
{
 CTemp tm(10,20);
 CTemp sum = 1000;
 printf("Sum = %d /n",tm.GetSum(sum));
}

----------------------------------------------------------
Output:
Construct function!
a = 10
b = 20
Construct function!
a = 1000
b = 0
Sum = 1000
----------------------------------------------------------
わずかに変更するだけで、一時的なオブジェクトの作成が1回減少します.
1,このときの"="号は元の付与値から構造に変わる.
2,Sumの構造が遅れています.CTmep sumを定義すると、mainのスタックにsumオブジェクトの予約されたスペースが作成されます.1000で構造を呼び出すと,このときの構造はsumのために予約された空間で行われる.これにより、一時的なオブジェクトの作成も一度減少します.
 
 
三、関数はオブジェクトを返します.
 
関数がオブジェクトを返す必要がある場合、スタックに一時オブジェクトを作成し、関数の戻り値を格納します.次のコードを参照してください.
  #include <stdio.h>
class CTemp
{
public:
    int a;
public:
    CTemp(CTemp& t) //Copy Ctor!
    {
        printf("Copy Ctor!/n");
        a = t.a;
    };
    CTemp& operator=(CTemp& t) //Assignment Copy Ctor!
    {
        printf("Assignment Copy Ctor!/n");
        a = t.a;
        return *this;
    }
    CTemp(int m = 0);
    virtual ~CTemp(){};
};
CTemp::CTemp(int m) //Copy Ctor!
{
    printf("Construct function!/n");
    a = m;
    printf("a = %d/n",a);
}
CTemp Double(CTemp& ts)
{
    CTemp tmp;      //
    tmp.a = ts.a*2;
    return tmp;
    
}
//-------------Main -----------------
void main()
{
    CTemp tm(10),sum;
    printf("/n/n");
    sum = Double(tm);     
    printf("/n/nsum.a = %d /n",sum.a);
}

---------------------------------------------------------
Output:
Construct function!
a = 10
Construct function!
a = 0
Construct function!
a = 0
Copy Ctor!
Assignment Copy Ctor!
sum.a = 20
--------------------------------------------------------
私はわざわざ文を広げました.
    sum = Double(tm);
この文は2つのオブジェクトを生成しました.Horrible!この文を徐々に分解します.
1では、tmp一時オブジェクトを明示的に作成します.
文:CTemp tmp;
2,tempオブジェクトを返し、戻る過程でCopy cotrを呼び出して戻りオブジェクトを作成し、
文:return tmp;
 
3,戻り結果を付与コピー関数を呼び出すことによりsumに付与する
文:sum=関数の戻り値;(このステップではオブジェクトは作成されず、sumに値を割り当てるだけです)
 
tm.Doubleはコピーコンストラクション関数で生成する一時オブジェクトを返し、その一時オブジェクトでsumに値を付与する.
上記の手順1で作成するオブジェクトは作成する必要はありません.戻り値を直接操作することができます.C++コンパイラの中には(NRV、named return value)という最適化があります.しかし、本人が使用しているVC++6.0は、この最適化を有効にしていません.
ステップ2で作成した戻りオブジェクトは避けられません.参照を返すことができると思いますが、関数で作成したローカルオブジェクトを忘れずに、戻り時に破棄されます.このとき、オブジェクトを再参照すると、予期せぬ動作が発生します.(C#でこの問題を解決しました).
 
解決方法:
オブジェクトの直接操作(Manipulate)をオブジェクトに戻し、上の一時オブジェクトを減らす方法と組み合わせて、関数Doubleのコードとmain関数のコードを以下のように変更します.
  CTemp Double(CTemp& ts)
{
    return ts.a*2;
}
/*-------- -------
CTemp _ret
void Double(CTemp& ts)
{
    _ret.a = ts.a*2;
}
---------------*/

//---------Main -----------
void main()
{
    CTemp tm(10);
    printf("/n/n");

    CTemp sum = Double(tm);
    
    printf("/n/nsum.a = %d /n",sum.a);
}

--------------------------------------------------------
Output:
Construct function!
a = 10
Construct function!
a = 20
sum.a = 20
-------------------------------------------------------
1回のコンストラクタ呼び出し(tmp)、1回のコピーコンストラクタ呼び出し(tmpが戻りオブジェクトにコピー)および1回の付与コピー関数呼び出しが減少することが分かった.(Assignment Copy Ctor)
戻りオブジェクトはsumのために予約する空間を直接使用するので、戻り一時オブジェクトの生成である戻りオブジェクトはsumであり、戻りオブジェクトの作成はsumオブジェクトの作成である.なんとすばらしいことか.
 
 
 
 
 
 
 
 
Jerry_Chow
2007/06/07