で作成された配列の一部だけを削除することはできませんnew
。new
すべて一緒にしかdelete
編集できないメモリのブロックを割り当てます。
オブジェクトがそれ自体の内部データを解放するようにしたい場合は、クラスを調整する必要があります。クラスは、それ自体を実行するために、それ自体の内部リソースをカプセル化して非表示にする必要があります。
割り当てた配列を保持するメモリの小さなブロックが必要な場合は、小さなブロックを割り当て、保持する内容を新しいブロックに移動してから、古いブロック全体を削除する必要があります。
int *arr = new int[10];
int *tmp = new int[9];
std::copy(arr+1, arr+10, tmp);
delete [] arr;
arr = tmp;
独自のリソースを管理し、コピーまたは移動を処理するようにクラスを設計する必要があります。現在myClass
は配列を割り当てていますが、クリーンアップを処理するために他のコードに依存しています。これを実行するのに適した方法ではありません。正しいことを実行するのに適したコードが他にない場合が多く、場合によっては間違いを犯すことが非常に多いためです。
コンストラクタで割り当てているので、割り当て解除を処理するデストラクタが必要です。そして、3つの特別な操作(copy-ctor、copy-assignment、destructor)のいずれかを実装するので、それらすべての実装を検討する必要があります。(これは「三つのルール」と呼ばれます。C++ 11では、ムーブ代入とムーブ代入が追加された「三つのルール」になります。)
class myClass {
public:
myClass();
// destructor to handle destroying internal resources correctly
~myClass();
// copy constructor and copy assignment operator, to handle copying correctly
myClass(myClass const &rhs);
myClass &operator=(myClass const &rhs);
// move constructor and move assignment operator, to handle moves correctly (C++11)
myClass(myClass && rhs);
myClass &operator= (myClass &&rhs);
private:
int *num1; // private so external code can't screw it up
public:
// limited access to num1
int size() const { if (num1) return 1; else return 0; }
int &operator[] (size_t i) { return num1[i]; }
};
コンストラクターを実装したのと同じように実装することも、初期化子リストとC++11の統一初期化を使用することもできます。
myClass::myClass() : num1(new int[1]{10}) {}
ここで、必要なデストラクタは、クラスに持たせたいセマンティクスと、維持したい特定の不変条件によって異なります。「値」セマンティクスはC++の標準です(JavaまたはC#に精通している場合、これらの言語はユーザー定義型の「参照」セマンティクスを推奨または要求します)。値のセマンティクスが必要な場合、およびnum1が常にメモリを所有するか、nullであるという不変条件を維持する場合に使用できる、デストラクタを次に示します。
myClass::~myClass() { delete num1; }
コピーと移動はさまざまな方法で処理できます。それらを完全に禁止したい場合は、(C ++ 11で)次のように言うことができます。
myClass::myClass(myClass const &rhs) = delete;
myClass &myClass::operator=(myClass const &rhs) = delete;
myClass::myClass(myClass && rhs) = delete;
myClass &myClass::operator= (myClass &&rhs) = delete;
または、コピーや移動を許可する(そして、上記の値のセマンティクスと不変条件を維持する)場合は、これらの関数のペアのいずれかまたは両方を実装できます。
myClass::myClass(myClass const &rhs) : num1( rhs.size() ? new int[1]{rhs[0]} : nullptr) {}
myClass &myClass::operator=(myClass const &rhs) {
if (num1)
num1[0] = rhs[0];
}
myClass::myClass(myClass && rhs) : num1(rhs.num1) { rhs.num1 = nullptr; } // remember to maintain the invariant that num1 owns the thing it points at, and since raw pointers don't handle shared ownership only one thing can own the int, and therefore only one myClass may point at it. rhs.num1 must be made to point at something else...
myClass &myClass::operator= (myClass &&rhs) { std::swap(num1, rhs.num1); } // steal rhs.num1 and leave it to rhs to destroy our own num1 if necessary. We could also destroy it ourselves if we wanted to.
int
この実装により、myClassオブジェクトをまたは他の「値」型と同じように扱うことができるようになりました。内部リソースの管理について心配する必要はもうありません。それ自体が面倒を見てくれます。
int main() {
std::vector<myClass> myclassvec(10);
cout << myclassvec[0][0] << '\n';
myclassvec.erase(myclassvec.begin()); // erase the first element
cout << myclassvec[0][0] << '\n'; // access the new first element (previously the second element);
}