2017-09-26 104 views
1

在下面的代碼中,我有一個具有int的動態數組的類A. 我有另一個類B有一個指向類AI的對象的指針數組已經寫入類A的拷貝構造函數。我需要爲B類寫一個拷貝構造函數和析構函數,我嘗試了各種方法,但沒有成功。 A級如何在一個類中創建複製構造函數和析構函數,其中對象本身有一個指向數組的指針數組

定義:

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 ;  
} 

B類的定義:

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數據成員,您使用一個擁有原始指針存儲到一個動態分配的數組。將其替換爲RAII資源管理器,如標準std::vector容器(例如std::vector<int> arr)。

這樣做,就沒有必要定義一個明確的析構函數,因爲編譯器將自動調用你的矢量數據成員的std::vector析構函數,和內存將自動釋放是(沒有你調用一個明確delete[])。

類似地,默認複製構造會做構件明智拷貝,所以std::vector拷貝構造將是自動由C++編譯器,以及從所述源向量的深拷貝到目標矢量調用將自動發生沒有你「重新發明輪子」與new[]動態分配,memcpy

與C++ 11開始,你可以告訴C++編譯器,合成使用這種語法默認的拷貝構造函數(可也爲默認的構造函數,移動構造函數,複製賦值運算符等)

class A { 
public: 
    ... 
    A(const A&) = default; 

    ... 
}; 

這同樣適用於class B,代替A * array[constant]數據構件,可以考慮使用vector,例如智能指針的矢量爲A:

vector<shared_ptr<A>> arrayOfAs; 

另一種可能使用std::array爲某物恆定的大小。

無論如何,關鍵是:考慮使用RAII資源管理器作爲更復雜類的構建塊,而不是使用原始指針和其他不安全的原始資源作爲數據成員。每個原始資源應該安全地包裝在自己的RAII資源管理器中,而這又應該用作更復雜類的數據成員。


P.S.作爲獎勵閱讀考慮閱讀"What is The Rule of Three? ",和這種follow up

+0

我想知道這有多少好在其他情況下)答案將起居者明白..:/ – gsamaras

+0

@gsamaras我不知道。但我知道我盡了最大的努力寫出了一份清晰明確的答案(在我的時間限制內),並嘗試給OP提供一些有用的幫助。 –

0

我們首先假設對於此練習,無論出於何種原因,您都不能使用std::vectorstd::array。假設我們按照你的類的設計,到目前爲止,我們可以實現一個拷貝構造函數是這樣的:

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]); 
    } 
} 

此所做的,array是一個指針到A秒的陣列,是在每一個元素動態分配內存使用new指向這些動態分配的A對象的指針,同時還使用語法new A(other_a)(調用A::A(const A &other))調用您爲A所做的複製構造函數來填充數組。由於other是對A的引用,而不是指向A的指針,因此保留在other.array[i]中的指針被取消引用,這就是爲什麼呼叫是A(*other.array[i])而不是A(other.array[i])

由於我們在這裏分配了動態存儲器new,析構函數必須調用delete來調用`new'。這同樣可以實現這樣:

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執行的分配失敗並引發異常會發生什麼?或者如果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; 
    } 
} 

這樣的代碼很快就會變得無法維護。爲了避免這樣的問題,一個很好的指導原則是管理一個必須被創建和銷燬的資源的生命週期應該由一個只管理該資源的生命週期的類來封裝。這很重要,因爲如果你開始向類中添加更多類似於這個數組的類,那麼你的類將負責構造和破壞更多的數組,這會使異常安全性比以前更加困難。實際上,構造和破壞數組的原因已經相當複雜,因爲您的類負責7個獨立資源(動態分配的對象)的生命週期,每個數組元素一個。

考慮到這一點,一種簡化這種方法的方法是使用一個封裝動態分配和釋放對象的類,其中包含newdelete。 C++ 11包含至少封裝釋放部分的幾個類,最相關的是std::unique_ptrstd::shared_ptr。然而這些類是爲避免複製而設計的。 unique_ptr是明確不可複製的,而複製shared_ptr僅創建對同一資源的新引用,同時保留引用計數。這意味着您仍然需要手動執行復制。

A *array[constant]; 

到:

你可以通過改變你的聲明中B從切換到unique_ptr

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

然後,你可以填充每個成員在您的拷貝構造函數:

array[i] = std::unique_ptr<A>(new A(*other.array[i])); 

用這種方法,你不會再不必擔心捕獲異常,因爲如果在構造函數中某處拋出異常,將自動爲數組中的每個unique_ptr調用析構函數。尚未分配到的unique_ptr默認情況下會保留空指針,並且在銷燬時不會安全地執行任何操作。

然而,還有一種方法:根本不使用指針/動態內存。你已經有一個類(A),它負責自己的資源的生命週期。

A *array[constant]; 

到:

要做到這一點,在B以下聲明可從改變

A array[constant]; 

這將意味着你不再需要定義一個拷貝構造函數(或複製根本不在意。如果在C++類中沒有提供複製構造函數,則可以複製該類,就好像它具有簡單的成員複製構造函數,該構造函數也適用於數組,並且將爲數組中的每個元素調用複製構造函數。由於數組本身是類的一部分並且不包含指向動態內存的指針,因此不需要使用delete手動解除分配每個元素。

相關問題