2017-09-26 8 views
1

以下のコードでは、intの動的配列を持つクラスAがあります。 クラスAのオブジェクトへのポインタの配列を持つ別のクラスBがすでにクラスAのコピーコンストラクタを記述しています。 クラスBのコピーコンストラクタとデストラクタを記述する必要がありますが、 。クラスAのオブジェクトにポインターの配列を持つクラスでコピーコンストラクターとデストラクタを作成する方法オブジェクト自体がintへのポインタ配列

定義:クラスBの

class A { 
    public: 
    A::A(const A& other){ 
     siz = other.siz; 
     c = other.c; 
     s = other.s; 
     e = other.e; 
     arr= new int[other.c]; 
     memcpy(arr, other.arr, sizeof(int) * c); 
    } 
    A::~A() { 
     delete [] m_arr; 
    } 

    const A& operator=(const A& rhs){ 

     if(this == &rhs) 
      return *this; // handling of self assignment 

     delete[] arr; // freeing previously used memory 

     arr = new int[rhs.c]; 
     siz = rhs.siz; 
     c = rhs.c; 
     e = rhs.e; 
     s = rhs.s; 

     memcpy(m_arr, rhs.arr, sizeof(int) * c); 
     return *this; 
    } 

    private : 
    int *arr ;   
    int c ; 
    int siz ;  
    int s ;  
    int e ;  
} 

定義:助けを

class B { 

     public: 
     B::B(const B& other){ 
      // .......need help here 
     } 

     B::~B() { 
      //......need help here 
     } 

     private : 
     static const int constant = 7; 
     A * array[constant] ;   
     int x ; 
     int y ;  
     int z ;   
} 

おかげで

+6

なぜ 'std :: vector'を使用しないのですか? – user463035818

+0

B :: B(const A&other){、なぜここにクラスAがあるのですか?それはクラスBでなければなりません – Sumeet

+0

Aのための代入演算を書くと助けます – doctorlove

答えて

1

キーは、生RAIIリソースへポインタを所有しているラップすることです管理者、およびこれらの安全なRAIIビルディングブロックを組み立てるクラスを定義します。その後、C++コンパイラは自動的にになります。は、コピー操作とデストラクタを合成します(操作も移動します)。

たとえば、のあなたclass Aあなたは、動的に割り当てられた配列へ所有生のポインタを格納するのに使用することを、int *arrデータメンバを持っています。標準std::vectorコンテナ(例:std::vector<int> arr)のようなRAIIリソースマネージャーで置き換えます。そう

、(明示的なdelete[]を起動せずに)コンパイラはが自動的があなたのベクトルデータメンバにstd::vectorデストラクタを呼び出しますし、メモリは自動的発売さになるように明示的なデストラクタを定義する必要は、ありません。

同様に、デフォルトのコピーコンストラクタは、メンバーごとのコピーを行いますので、std::vectorコピーコンストラクタが自動的には、C++コンパイラによって呼び出されると、デスティネーションベクタにソースベクトルからディープコピーが自動的に行われますmemcpyなど

C++ 11以降では、C++コンパイラに、この構文でデフォルトのコピーコンストラクタを合成するよう指示できます(デフォルトのコンストラクタでも使用可能ですが、コピーコンストラクタ、コピー代入演算子など)

代替の何かのために std::arrayを使用している可能性があり

vector<shared_ptr<A>> arrayOfAs; 

同じことがclass Bのために行く、代わりにA * array[constant]データメンバの、例えば、へスマートポインタのベクトルをvectorを使用することを検討してください一定の大きさ。

とにかく、生存所有ポインタとその他の安全でないRAWリソースをデータメンバーとして持つ代わりに、RAIIリソースマネージャをより複雑なクラスのビルディングブロックとして使用することを検討してください。各RAWリソースは、RAIIリソースマネージャーで安全にラップされる必要があります。これは、より複雑なクラスのデータメンバーとして使用する必要があります。


P.S.ボーナスとして、"What is The Rule of Three? "、この種のfollow upをお読みください。

+0

他の場合は良い)答えは初心者が理解するでしょう:/ – gsamaras

+0

@gsamaras分かりません。しかし、私は(私の時間制約の中で)はっきりと精巧な答えを書くために最善を尽くし、OPに役立ついくつかの助けをしようとしていることを知っています。 –

0

まずこの演習では、何らかの理由でstd::vectorまたはstd::arrayを使用できないとしましょう。 arrayA Sへのポインタの配列があるようで、これは何

B::B(const B &other) 
{ 
    for (std::size_t i = 0; i < constant; ++i) { 
     // Use `new` to allocate memory and also call `A`'s copy constructor. 
     array[i] = new A(*other.array[i]); 
    } 
} 

すべての要素での動的メモリを割り当てる:私たちはこれまで、あなたのクラスの設計に従うと仮定すると、我々はこのようなコピーコンストラクタを実装することができますこれらのオブジェクトへのポインターをnewで動的に割り当てられたAオブジェクトへのポインタで置き換え、Aに対して作成したコピーコンストラクターを(A::A(const A &other)を呼び出す)を使用して呼び出します。 AへのポインタではなくAへの参照であるため、other.array[i]に保持されているポインタは逆参照されるため、A(other.array[i])ではなくA(*other.array[i])となります。

newでここに動的メモリを割り当てたので、デストラクタは `newへの呼び出しごとにdeleteを呼び出す必要があります。それでは、私たちが今持っていることは、私たちが望むように動作するように何かが

B::~B() 
{ 
    for (std::size_t i = 0; i < constant; ++i) { 
     // As each `A` has been allocated with `new`, they should now be 
     // destroyed. 
     delete array[i]; 
    } 
} 

であり、我々はそれはそれでお終いです仮定することができる:これはそうと同様に実施することができます。しかし、newによって実行された割り振りの1つが失敗して例外をスローするとどうなるでしょうか?または、Aのコンストラクタが例外をスローした場合はどうなりますか? deleteは、これまでにnewで割り当てられた要素に対して決して呼び出されません。

コピーコンストラクターの例外を安全にするには、少し複雑なコードが必要です。例えば、このような何か:このような

B::B(const B &other) 
{ 
    std::size_t i; 

    try { 
     for (i = 0; i < constant; ++i) { 
      array[i] = new A(*other.array[i]); 
     } 
    } catch (...) { 
     // Delete all elements allocated so far 
     for (std::size_t d = 0; d < i; ++d) { 
      delete array[i]; 
     } 

     // Re-throw the exception to the caller 
     throw; 
    } 
} 

コードはすぐにunmaintainableになることができます。このような問題を回避するには、作成および破棄する必要があるリソースのライフタイムを管理することは、その1つのリソースの存続期間のみを管理するクラスでカプセル化する必要があります。これは、この配列に似たクラスに多くの構造体を追加し始めた場合、クラスだけがこの配列以外の構造体を構築して破壊することになり、例外安全性をそれよりもさらに困難にするからです。実際には、あなたのクラスが7つの別々のリソース(動的に割り当てられたオブジェクト)の寿命を担当し、各配列要素に1つずつ責任を負うため、配列の作成と破棄の理由はかなり複雑です。

これを念頭に置いて、これを簡略化する方法は、newdeleteというオブジェクトを動的に割り当てたり割り当てを解除したりするクラスを使用することです。 C++ 11には少なくとも割り当て解除部分をカプセル化するいくつかのクラスがあり、最も関連性の高いのはstd::unique_ptrstd::shared_ptrです。しかし、これらのクラスはコピーを避けるように設計されています。 unique_ptrは明示的にコピーできません。shared_ptrをコピーすると、参照カウントを保持したまま、同じリソースへの新しい参照が作成されます。つまり、手動でコピーを実装する必要があります。

あなたはからBであなたの宣言を変更することにより、unique_ptrに切り替えることができます:

array[i] = std::unique_ptr<A>(new A(*other.array[i])); 
:次に、あなたとあなたのコピーコンストラクタで各メンバーを移入でき

std::unique_ptr<A> array[constant]; 

:に

A *array[constant]; 

この方法では、もはやコンストラクタのどこかに例外がスローされた場合、配列内の各unique_ptrに対してデストラクタが自動的に呼び出されるため、例外をキャッチすることについて心配する必要があります。まだ割り当てられていないunique_ptrはデフォルトでヌルポインタを保持し、破壊されても安全に何も行いません。

ただし、ポインタ/ダイナミックメモリをまったく使用しない方法があります。あなた自身のリソースの寿命を担うクラス(A)が既にあります。

A array[constant]; 

これは、あなたは、もはやコピーコンストラクタを定義する必要があることを意味しないだろう(またはコピー:

A *array[constant]; 

へ:

これを行うには、Bで次の宣言から変更することができます代入演算子)を使用します。コピーコンストラクタがC++クラスで提供されていない場合、クラスは単純なメンバーワイズコピーコンストラクタを持つかのようにコピーできます。コンストラクタは配列でも機能し、配列の各要素のコピーコンストラクタを呼び出します。また、配列自体はクラスの一部であり、動的メモリへのポインタを保持しないため、各要素を手動でdeleteの割り当てを解除する必要はありません。

関連する問題