web-dev-qa-db-ja.com

クラスのインスタンスを手動で削除するにはどうすればよいですか?

クラスのインスタンスを手動で削除するにはどうすればよいですか?

例:

#include <iostream>
#include <cstring>

class Cheese {
private:
    string brand;
    float cost;
public:
    Cheese(); // Default constructor
    Cheese(string brand, float cost); // Parametrized constructor
    Cheese(const Cheese & rhs); // Copy construtor
    ~Cheese(); // Destructor
    // etc... other useful stuff follows
}

int main() {
    Cheese cheddar("Cabot Clothbound", 8.99);
    Cheese swiss("Jarlsberg", 4.99);

    whack swiss; 
    // fairly certain that "whack" is not a keyword,
    // but I am trying to make a point. Trash this instance!

    Cheese swiss("Gruyère",5.99);
    // re-instantiate swiss

    cout << "\n\n";
    return 0;
}
4
kmiklas

解決したいユースケースや実際の問題がわからなくても( XY問題 について読んでください。あなたの質問はその良い例です)、最も簡単な方法は単に再割り当てすることです。

Cheese swiss("Jarlsberg", 4.99);
...
swiss = Cheese("Gruyère",5.99);

もちろん、代入演算子を実装する必要があるかもしれませんが、 または5のルール に従う必要があります(ただし、 のルールに従う場合は代入演算子は必要ありません)ゼロ )。

現在のswissオブジェクトを明示的に破棄したい場合は、couldもポインタを使用できます。

Cheese* swiss = new Cheese("Jarlsberg", 4.99);
...
delete swiss;
swiss = new Cheese("Gruyère",5.99);

しかし、ポインタは避けるべきワームの缶であり、最近のC++ではそれほど必要ありません。ただし、ポリモーフィズムが必要な場合は、ポインター(または参照)が必要です。次に、実際のインスタンスを指す基本クラスへのポインターを設定できます。仮想関数などは期待どおりに機能します。

また、まだ何も知らない状況に応じて、もちろんスコープを使用できます。

Cheese swiss("Jarlsberg", 4.99);
...
{
    Cheese swiss("Gruyère",5.99);
    // In here the swiss cheese is a Gruyère
    ...
}
// Out here the swiss cheese is a Jarlsberg

このように変数名をシャドウイングすることは機能しますが、コードの読者に混乱をもたらすため、避けるべき悪い習慣です。一方、スコープを使用している場合でも、必要な(有効な)変数名を使用することを妨げるものは何もないため、外部スコープインスタンスにjarlsberg、内部スコープインスタンスにgruyereという名前を付けることができます。 gruyereオブジェクトは、他のネストされたスコープ変数が破棄されて「消える」のと同じように、スコープの最後で破棄されます。

スコープを使用して、クラスの別のインスタンスを定義できます。

Cheese swiss("Toe", 3.14)

{
    Cheese swiss("Ear", 15.9);
}

原則として、ローカルで宣言されたインスタンスは、スコープ外になると自身を破棄します。

チーズを破壊する必要性を本当に満たす場合は、代わりに動的に割り当てる必要があります。

  Cheese *swiss = new Cheese("toe", 3);

   // do something with swiss.

   delete swiss;    // throw it away.

   swiss = new Cheese("Ear", 7);

   // do something with swiss.

   delete swiss;    // throw it away.

動的に割り当てられたメモリは、常に手動で削除する必要があります。

4
EvilTeach

これを行う必要がある貴重な例はほとんどありません。しかし、遭遇する可能性のあるものの1つは、抽象データ型を作成するときです。

たとえば、バリアント型を作成する場合は、整列されたデータ型を設定してから、手動でnewを配置して削除する必要があります。

typename std::aligned_union<0, FirstType, RestTypes...>::type m_buffer;

生き返らせるために:

new (&m_buffer) AssignType(forward<T>(x));

クリアするには:

(HeldType*)(&m_buffer)->~HeldType();

ただし、他の多くの投稿で言及されているように。通常のプログラミングをしている場合は、手動でdtorを呼び出すことを心配する必要はありません。スタック上にある場合は、クリーンアップされます。ヒープ上にある場合は、deleteが自動的に処理します。これを実行するのは、オブジェクトの存続期間を手動で制御する場合のみです。これを実行する主な理由は、抽象データ型を実装する場合です。

2

C++のダークサイドは、元の投稿で説明したことを正確に実行するための手法を示しています。なぜこれを実行したいのかわかりません。代入演算子を実装するのは面倒なようです。たぶんあなたはあなたのプログラムに不自然なことをしたいという病的な欲求を持っています。少なくとも他の人が見ているときは、「普通の」人が以下に示すコードの使用を検討しないことを保証できます。では、なぜこれをここに置くのですか?

私はサディスティックだからです。

猛攻撃から私を守るために、コミュニティWikiの力を呼びかけます!

#include <iostream>
#include <string>
#include <new>

template <typename T, typename ...As>
inline void Reconstruct(T &ojt, const As&... ctor_args) {
    // Explicitly call the destructor, to destroy the object
    // without doing anything to its memory allocation.
    ojt.~T();

    // Use Placement new to call a constructor.
    // Instead of allocating memory somewhere for a new object,
    // this specifies where the new object will be constructed --
    // given here as the location of the object's previous
    // incarnation.
    // Also pass any arguments to the constructor.  I forced
    // these arguments to be const references in order to
    // avoid extra copying, so "move" construction won't work
    // and steps might need to be taken to accommodate other,
    // more unusual constructors.
    new(&ojt) T(ctor_args...);
}

class Cheese {
    std::string brand;
    float       cost;
public:
    Cheese() : cost(0) {}
    Cheese(std::string brand, float cost) : brand(brand), cost(cost) {}
    Cheese(const Cheese & rhs) = default;
    ~Cheese() = default;
    friend std::ostream& operator << (std::ostream& os, const Cheese& chz) {
        return os << "[brand:\"" << chz.brand << "\" cost:" << chz.cost << ']';
    }
};

int main() {
    Cheese cheese, fromage;
    std::cout << "cheese = " << cheese << '\n';

    Reconstruct(cheese, "Cabot Clothbound", 8.99f);
    std::cout << "cheese = " << cheese << '\n';

    Reconstruct(fromage, cheese);
    std::cout << "fromage = " << fromage << '\n';

    Reconstruct(cheese, "Jarlsberg", 4.99f);
    std::cout << "cheese = " << cheese << '\n';
}
0

何らかの理由で代入演算子を使用できない場合は、optionalを使用できます。

std::experimental::optional<Cheese> swiss(std::experimental::in_place, "Jarlsberg", 4.99);

swiss = std::experimental::nullopt; // Calls Cheese::~Cheese internally

// re-instantiate swiss
swiss.emplace("Gruyère",5.99);

オプションを保存していない限り、コンパイラが余分な内部ブール値を最適化することに頼ることができます。

0
sbabbi