2

私はこれが答えられたと確信していますが、私はプログラマーではなく、適切な答えを見つける/理解することができませんでした。bigたとえば、二項演算子をオーバーロードしたい大規模なクラスがあるとしますoperator +

  1. 一時的なオブジェクトを作成し、それをコピーして一時的なオブジェクトを破棄する代わりにbig X=Y+Z、合計を直接に組み込むための正しい方法はありますか?XX
  2. 私の頭に浮かぶ唯一のことは、bigへのポインタを含み、bigへの参照の数を追加bigする別のクラスにラップして、オブジェクトが。のときに破棄されるようにすることです。そして、実際のコピーのために、別の代入演算子を追加します。私はそれを実装しようとしました(以下)。うまくいくようですが、経験がなく、何がうまくいかないのかを予見するのは難しいです。もっと簡単な解決策があるべきではありませんか?smallbigint use;biguse==0<=

コード:

#include <iostream>

// print and execute cmd
#define Do(cmd) cout << "\n\n\t"<< ++line << ".\t" << #cmd << ";\n" << endl; cmd;

// print small object: name(small.id[big.id,u=big.use,x=big.x])
#define Show(avar) cout << #avar << "(" << (avar).id << "[" << ((avar).data==NULL?0:(avar).data->id) << ",u=" << ((avar).data==NULL?0:(avar).data->use) << ",x=" << ((avar).data==NULL?0:(avar).data->x) << "])" 

using namespace std;

class big{
public:
  static int N;   // biggest id in use
  int id;         // unique id for each object
  int use;        // nuber of references to this object
  int x;          // data
  big() __attribute__((noinline))
  {
    id=++N;
    use=1;
    x=0;
    cout << "big.constructor.def: [" << id << ",u=" << use << ",x="<<x<<"]" << endl;
  }
  big(const int& y) __attribute__((noinline))
  {
    id=++N;
    x=y;
    use=1;
    cout << "big.constructor.int: [" << id << ",u=" << use << ",x="<<x<<"]" << endl;
  }
  big(const big& b) __attribute__((noinline))
  {
    id=++N;
    use=1;
    x=b.x;
    cout << "big.constructor.copy: [" << id << ",u=" << use << ",x="<<x<<"]" << endl;
  }
  ~big() __attribute__((noinline))
  {
    if(use>0) throw 99; // destroing referenced data!
    cout << "big.destructor: [" << id << ",u=" << use << ",x="<<x<<"]" << endl;
  }
  friend class small;
};

class small{
public:
  static int N;      // biggest id in use
  int id;            // unique id
  big * data;        // reference to the actual data
  small() __attribute__((noinline))
  {
    id=++N;        
    data=NULL;       // contains no data
    cout << "small.constructor.def: ";
    Show(*this)<< endl;
  }
  small(const int& y) __attribute__((noinline))
  {
    id=++N;
    data=new big (y);  // relies on the big constructor
    cout << "small.constructor.int: ";
    Show(*this)<<endl;
  }
  small(const small& y) __attribute__((noinline))
  {
    id=++N;
    data=y.data;      // new object refers to the same data!!
    if(data!=NULL) 
      ++(data->use);  // new reference added;
    cout << "small.constructor.copy: "; 
    Show(y) << "-->";
    Show(*this) << endl;
  }
  ~small(){
    cout << "small.destructor: ";
    Show(*this)<< endl;
    if(data!=NULL){       // is there data?
      --(data->use);      // one reference is destroyed
      if(data->use == 0)  // no references left, kill the data
    delete data;
    }
  }
  const small& operator= (const small& b) __attribute__((noinline))
  {
    cout << "equal: ";
    Show(*this) << " = ";
    Show(b)<<endl;
    if(data != NULL){     // is there data in the target?
      --(data->use);      // one reference is destroyed
      if(data->use == 0)  // no references left, 
    delete data;      // kill the data
    }
    data=b.data;          // target referenses the same data as the source!
    if(data!=NULL) 
      ++(data->use);      // new references added
    cout << "Done equal: "<<endl;    
    return *this;
  }
  // <= will be used for actual copying the data
  const small& operator<= (const small& b) __attribute__((noinline))
  {
    cout << "Copy: ";
    Show(*this) << " <= ";
    Show(b)<<endl;
    if(data != NULL){     // is there data in the target?
      --(data->use);      // one reference is destroyed
      if(data->use == 0)  // no references left, 
    delete data;      // kill the data
    }
    if(b.data==NULL)     // source has no data
      data=NULL;
    else
      data = new big(*(b.data)); // make new copy of the data 
                                 // via big's copy constructor
    cout << "Done copy: "<<endl;    
    return *this;
  }
  small operator+ (const small& b) __attribute__((noinline))
  {
    cout << "Plus: "; 
    Show(*this) << " + ";
    Show(b)<< endl;
    if(this->data == NULL | b.data == NULL) throw 99; // missing data for +
    small ret(data->x);
    ret.data->x += b.data->x;
    cout << "Return: "; Show(ret)<<endl;
    return ret;
  }
};


int big::N=0;
int small::N=0;

main(){
  int line=0;

  Do(small X(5); small Y(6); small Z(7); small W(X));
  Show(X) << endl;
  Show(Y) << endl;
  Show(Z) << endl;
  Show(W) << endl;

  Do(X=Y; Z<=Y);
  Show(X)<<endl;  
  Show(Y)<<endl;  // X and Y refer to the same data
  Show(Z)<<endl;  // Z has a copy of data in Y

  Do(X=Z; Y=Z);
  Show(X)<<endl;
  Show(Y)<<endl;
  Show(Z)<<endl;  // data previosly in X,Y destroyed

  Do(small* U=new small (17); small* T=new small (*U));
  Show(*U) << endl;
  Show(*T) << endl; // U and T refer to the same big

  Do(delete U);
  Show(*T) << endl; // big stays since there is another reference to it

  Do(delete T);     // big destroyed

  Do(X=(Y+Z)+W);
  Show(X)<<endl;
  Show(Y)<<endl;
  Show(Z)<<endl;  // no extra copying of data occures

  cout << "\n\tEND\n" << endl;
}

出力:

1.  small X(5); small Y(6); small Z(7); small W(X);

big.constructor.int: [1,u=1,x=5]
small.constructor.int: *this(1[1,u=1,x=5])
big.constructor.int: [2,u=1,x=6]
small.constructor.int: *this(2[2,u=1,x=6])
big.constructor.int: [3,u=1,x=7]
small.constructor.int: *this(3[3,u=1,x=7])
small.constructor.copy: y(1[1,u=2,x=5])-->*this(4[1,u=2,x=5])
X(1[1,u=2,x=5])
Y(2[2,u=1,x=6])
Z(3[3,u=1,x=7])
W(4[1,u=2,x=5])


    2.  X=Y; Z<=Y;

equal: *this(1[1,u=2,x=5]) = b(2[2,u=1,x=6])
Done equal: 
Copy: *this(3[3,u=1,x=7]) <= b(2[2,u=2,x=6])
big.destructor: [3,u=0,x=7]
big.constructor.copy: [4,u=1,x=6]
Done copy: 
X(1[2,u=2,x=6])
Y(2[2,u=2,x=6])
Z(3[4,u=1,x=6])


    3.  X=Z; Y=Z;

equal: *this(1[2,u=2,x=6]) = b(3[4,u=1,x=6])
Done equal: 
equal: *this(2[2,u=1,x=6]) = b(3[4,u=2,x=6])
big.destructor: [2,u=0,x=6]
Done equal: 
X(1[4,u=3,x=6])
Y(2[4,u=3,x=6])
Z(3[4,u=3,x=6])


    4.  small* U=new small (17); small* T=new small (*U);

big.constructor.int: [5,u=1,x=17]
small.constructor.int: *this(5[5,u=1,x=17])
small.constructor.copy: y(5[5,u=2,x=17])-->*this(6[5,u=2,x=17])
*U(5[5,u=2,x=17])
*T(6[5,u=2,x=17])


    5.  delete U;

small.destructor: *this(5[5,u=2,x=17])
*T(6[5,u=1,x=17])


    6.  delete T;

small.destructor: *this(6[5,u=1,x=17])
big.destructor: [5,u=0,x=17]


    7.  X=(Y+Z)+W;

Plus: *this(2[4,u=3,x=6]) + b(3[4,u=3,x=6])
big.constructor.int: [6,u=1,x=6]
small.constructor.int: *this(7[6,u=1,x=6])
Return: ret(7[6,u=1,x=12])
Plus: *this(7[6,u=1,x=12]) + b(4[1,u=1,x=5])
big.constructor.int: [7,u=1,x=12]
small.constructor.int: *this(8[7,u=1,x=12])
Return: ret(8[7,u=1,x=17])
equal: *this(1[4,u=3,x=6]) = b(8[7,u=1,x=17])
Done equal: 
small.destructor: *this(8[7,u=2,x=17])
small.destructor: *this(7[6,u=1,x=12])
big.destructor: [6,u=0,x=12]
X(1[7,u=1,x=17])
Y(2[4,u=2,x=6])
Z(3[4,u=2,x=6])

    END

small.destructor: *this(4[1,u=1,x=5])
big.destructor: [1,u=0,x=5]
small.destructor: *this(3[4,u=2,x=6])
small.destructor: *this(2[4,u=1,x=6])
big.destructor: [4,u=0,x=6]
small.destructor: *this(1[7,u=1,x=17])
big.destructor: [7,u=0,x=17]
4

4 に答える 4

3

あります、それはコピーの省略と呼ばれます。この場合に特に関連するのは、 名前付きの戻り値最適化(RVO)と戻り値最適化(NRVO)です。これは、特定の状況で値を返すときに、コンパイラーがコピーを削除できることを意味します。単純な加算演算子を実装すると、RVOが発生する可能性があります。

これはコンパイラーが実行できる最適化ですが、実行が保証されているわけではないことに注意してください。ただし、C ++には移動セマンティクスがあり、不要なコピーを発生させることなく、ある(通常は一時的な)オブジェクトの基になるデータを別のオブジェクトに「移動」できる正式な手段を提供します。ここに移動セマンティクスに関する記事があります。

于 2012-09-17T15:27:54.750 に答える
1

合計が複合値である場合、代替アプローチは、YおよびZへのポインターまたは参照を保持big::operator+するクラスのインスタンスを返すことです。sumOfBig

sumOfBig必要に応じて、その場で合計構成要素を計算するメンバー関数が含まれる場合があります。

于 2012-09-17T15:41:21.983 に答える
1

+=このような場合に定義して使用することを検討してください。

Big a, b, c;

それ以外の:

a = b + c;

行う:

a=b;
a+=c;

の定義例+=

Big& Big::operator += (const Big& other)
{
   this->a += other.a;
   // ...
   return *this;
}

あなたはそれらを論理的に同じにするためにあなたのoperator +に基づいて作ることができます。operator +=

于 2012-09-17T15:41:50.847 に答える
0

まず、最適化をオンにして、コンパイラが実際に何をするかを確認します。juanchopanzaの回答に従って、(N)RVOを無料で入手できます。

コンパイラがそれを実行できない場合は、Piotrの回答に従って中間コピーを明示的に除外すると、問題が改善される可能性があります。

任意に複雑な数式の評価を延期する必要がある場合は、式テンプレートが必要です(Nicolaとコメントで述べたように)。可変ソースオブジェクトがある場合は、式テンプレートコピーオンライトが必要になる場合があります。これらはどちらも些細なことではありません。本当に必要で、必要なことをすでに実行しているライブラリが本当に見つからない場合は、式テンプレートの実装に関する質問を探して、調査して開始します。

于 2012-09-17T16:02:10.383 に答える