如何手动删除类的实例?

时间:2016-03-24 02:55:50

标签: c++ class instance destructor kill

如何手动删除类的实例?

示例:

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

6 个答案:

答案 0 :(得分:8)

在不知道用例或您想解决的实际问题的情况下(请阅读the XY problem,您的问题就是一个很好的例子)最简单的方法就是重新分配:

swiss

这当然可能要求您实现赋值运算符,但是遵循rules of three or five无论如何都应该这样做(但如果您遵循the rule of zero则不需要赋值运算符。)

如果您明确要销毁当前的Cheese* swiss = new Cheese("Jarlsberg", 4.99); ... delete swiss; swiss = new Cheese("Gruyère",5.99); 对象, 也可以使用指针:

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

但是指针是你应该避免的一堆蠕虫,并且在现代C ++中并不需要太多。但是如果你想要多态性,则需要指针(或引用)。然后你可以有一个指向实际实例的基类的指针,像虚函数这样的东西将按预期工作。

另外,根据您的情况我们仍然不知道,您当然可以使用范围界定:

jarlsberg

虽然像这样的阴影变量名称有效,但是你应该避免这是一个坏习惯,因为它会给代码的读者增加混乱。另一方面,即使使用作用域,也不会阻止您使用任何(有效)变量名称,因此可以将外部作用域实例gruyere和内部作用域实例gruyere命名为{{然后,对象将在范围的末尾被破坏,就像任何其他嵌套范围变量将被破坏并“消失”一样。

答案 1 :(得分:4)

可以使用作用域来定义另一个类的实例。

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.

必须始终手动删除动态分配的内存。

答案 2 :(得分:2)

在极少数情况下您需要这样做。但是,您可能遇到的是创建抽象数据类型时。

例如,如果您正在制作变体类型,则可能需要设置对齐的数据类型,然后手动放置新的和删除。

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

为了生存:

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

要清除:

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

然而,如众多其他帖子所述。如果你正常编程,那么你不必担心手动调用dtors。如果它在堆栈上,那么它就会被清理干净。如果它在堆上,那么delete将为您处理它。您想要执行此操作的唯一时间是您手动控制对象生命周期,并且您要执行此操作的主要原因是您实现抽象数据类型时。

答案 3 :(得分:1)

一般规则 一旦超出范围,本地创建的实例将自动删除/处理。 需要手动删除动态创建的实例(动态内存分配),如下所示:

删除instance_name;

  • 注意 :请勿在〜cheese()(〜destructor)中包含delete instance_name语句,否则它将进入循环并崩溃

在上述手动删除实例的情况下,建议使用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;
}

答案 4 :(得分:0)

C ++的黑暗面带来了一种技术,可以准确地执行您在原始帖子中描述的内容。我不知道你为什么要这样做 - 也许实现一个赋值运算符似乎很乏味。也许你有一个病态的愿望,为你的程序做一些不自然的事情。我可以向你保证,没有&#34;正常&#34;人会考虑使用我在下面透露的代码,至少在别人看的时候不会。那我为什么要把它放在这里呢?

因为我很伤心。

我呼吁社区维基的力量保护我免受冲击!

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

答案 5 :(得分: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);

只要您没有存储可选项,您可以依赖编译器优化额外的内部布尔值。