kmiklas
kmiklas

Reputation: 13463

How do I manually delete an instance of a class?

How do I manually delete an instance of a class?

Example:

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

Upvotes: 9

Views: 63267

Answers (6)

saurabhsawant0873
saurabhsawant0873

Reputation: 11

A general Rule Locally created Instances are automatically deleted/taken care once it goes out of scope. Dynamically created Instances (Dynamic Memory Allocation) need to be deleted manually as follows:

delete instance_name;

  • Note : Do not include delete instance_name statement inside ~cheese() (~destructor), otherwise it will enter into a loop and crash

In above case where you manually want to delete the instance, it is recommended use DMA:

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

    delete swiss;


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

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

Upvotes: 1

sbabbi
sbabbi

Reputation: 11201

If for whatever reason you can not use the assignment operator, you can use an 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);

As long as you are not storing the optional, you can probably rely on the compiler optimizing out the extra internal bool.

Upvotes: 0

Christopher Oicles
Christopher Oicles

Reputation: 3107

The Dark Side of C++ posesses a technique for performing exactly what you describe in the original post. I don't know why you want to do this -- maybe implementing an assignment operator seems tedious. Maybe you have a morbid desire to do something unnatural to your program. I can assure you that no "normal" person would consider using the code I reveal below, at least not when others are watching. So why am I putting this here?

Because I'm sadistic.

I call upon the power of Community Wiki to shield me from the onslaught!

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

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409482

Without knowing the use-case or the actual problem you want to solve (please read about the XY problem, your question is a good example of it) the simplest way is just reassigning:

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

That might of course require you to implement an assignment operator, but following the rules of three or five you should do that anyway (but the assignment operator is not needed if you follow the rule of zero).

You could also use pointers, if you explicitly want to destroy the current swiss object:

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

But pointers is a can of worms that you should avoid, and don't really need much in modern C++. But pointers (or references) are needed if you want polymorphism. Then you could have a pointer to the base class pointing to the actual instance, and things like virtual functions will work as expected.

Also, and depending on your situation which we still know nothing about, you could of course use scoping:

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

Though shadowing variable names like this works, it's a bad habit that you should avoid as it adds confusion for readers of the code. On the other hand, even when using scopes nothing stops you from using any (valid) variable name you want, so you could name the outer scope instance jarlsberg and the inner scope instance gruyere, the gruyere object would then be destructed at the end of the scope just like any other nested-scope variable would be destructed and "disappear".

Upvotes: 12

OmnipotentEntity
OmnipotentEntity

Reputation: 17131

There are precious few instances where you would need to do this. But one that you might run into is when creating an abstract data type.

For instance, if you're making a variant type you'll probably want to set up an aligned data type and then manually placement new and delete.

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

To vivify:

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

To clear:

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

However, as mentioned in the numerous other posts. If you're programming normally, then you don't need to worry about manually calling dtors. If it's on the stack then it's cleaned up for you. If it's on the heap then delete will take care of it for you. The only time where you want to do this is where you're manually taking control over object lifetimes, and the main reason why you'd want to do this is when you're implementing an abstract data type.

Upvotes: 2

EvilTeach
EvilTeach

Reputation: 28882

One can use scoping to allow you to define another instance of a class.

Cheese swiss("Toe", 3.14)

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

As a general rule locally declared instances will destroy themselves when they go out of scope.

If you really feed the need to destroy cheese, then you need to dynamically allocate it instead.

  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.

Dynamically allocated memory must always be manually deleted.

Upvotes: 5

Related Questions