まずこの演習では、何らかの理由でstd::vector
またはstd::array
を使用できないとしましょう。 array
がA
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つずつ責任を負うため、配列の作成と破棄の理由はかなり複雑です。
これを念頭に置いて、これを簡略化する方法は、new
とdelete
というオブジェクトを動的に割り当てたり割り当てを解除したりするクラスを使用することです。 C++ 11には少なくとも割り当て解除部分をカプセル化するいくつかのクラスがあり、最も関連性の高いのはstd::unique_ptr
とstd::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
の割り当てを解除する必要はありません。
なぜ 'std :: vector'を使用しないのですか? – user463035818
B :: B(const A&other){、なぜここにクラスAがあるのですか?それはクラスBでなければなりません – Sumeet
Aのための代入演算を書くと助けます – doctorlove