1

次のシナリオで不要なコピーを回避するにはどうすればよいですか? クラス A には、大きなオブジェクトへの基本型ポインターが含まれています。

class A{
  BigBaseClass *ptr;
  A(const BigBaseClass& ob);
  ~A(){delete ptr;}
};

オブジェクトobをコピーする必要がある場合があります。そこで、仮想クローニングを実装します。

class BigBaseClass{
   virtual BigBaseClass* clone() {return new BigBaseClass(*this);}
};
class BigDerivedClass : BigBaseClass{
  virtual BigDerivedClass* clone() {return new BigDerivedClass(*this);}
};
A::A(const BigBaseClass& ob):ptr(ob.clone(){}

しかし、一時的な BigDerivedClass オブジェクトを作成し、それを使用してクラス A を構築することがあります。

A a{BigDerivedClass()};

また

BigDerivedClass f(){
     BigDerivedClass b;
     /*constructing object*/
     return b;
   }
   A a{f()};

ここでは、作成したオブジェクトをコピーしてから削除する必要はありません。このオブジェクトをヒープに直接作成し、そのアドレスをa.ptrに保存することができます。

しかし、コンパイラがここでコピー省略を実装するのに十分スマートであるとは思えません (またはそうですか?)。では、そのような不必要なコピーを避けるために何を提案しますか?

4

2 に答える 2

4

コンパイラは、 を介してコピーの構築を省略しませんclone()。コピーの省略は、非常に特殊な状況でのみ許可されます。コンパイラがコピー省略を許可されている場合は常に、関連するオブジェクトの有効期間はコンパイラによって完全に制御されます。4 つの状況は次のとおりです (詳細については、12.8 [class.copy] パラグラフ 8 を参照してください)。

  1. ローカル名を値で返します。
  2. ローカル オブジェクトをスローします。
  3. 参照にバインドされていない一時オブジェクトのコピー。
  4. 値でキャッチする場合。

これらの状況でもコピー省略が適用される場合の詳細は、やや重要です。いずれにせよ、return new T(*this);これらの状況のいずれにも適合しません。

通常のビッグ オブジェクトは、データをオブジェクトの一部として保持しません。代わりに、それらは通常、移動可能ないくつかのデータ構造を保持します。A{f()}の結果をコピーせずに を使用するときに単純さを維持したい場合は、コンテンツをコピーする代わりに、コンテンツを転送する関数f()を呼び出す move コンストラクターを使用できます。virtual

#include <utility>

class BigBaseClass {
public:
    virtual ~BigBaseClass() {}
    virtual BigBaseClass* clone() const = 0;
    virtual BigBaseClass* transfer() && = 0;
};
class A{
    BigBaseClass *ptr;
public:
    A(BigBaseClass&& obj): ptr(std::move(obj).transfer()) {}
    A(BigBaseClass const& obj): ptr(obj.clone()) {}
    ~A(){delete ptr;}
};
class BigDerivedClass
    : public BigBaseClass {
    BigDerivedClass(BigDerivedClass const&); // copy the content
    BigDerivedClass(BigDerivedClass&&);      // transfer the content
    BigDerivedClass* clone() const { return new BigDerivedClass(*this); }
    BigDerivedClass* transfer() && { return new BigDerivedClass(std::move(*this)); }
};

BigDerivedClass f() {
    return BigDerivedClass();
}

int main()
{
    A a{f()};
}

ムーブ コンストラクションが大きなオブジェクトのコピーに役立つかどうかは、オブジェクトが内部でどのように実装されているかに依存します。オブジェクトに実際の大きなデータへのポインターが基本的に 2 つだけ含まれている場合、ポインターの転送は実際のデータのセットアップに比べて無視できるため、ムーブ コンストラクションは関連するコストを回避する必要があります。データが実際にオブジェクト内に保持されている場合、転送は実際には役に立ちません (ただし、一般的には、さまざまな理由からそうするのは悪い考えです)。

于 2016-07-03T23:08:20.140 に答える