如何手动删除类的实例

How do I manually delete an instance of a class?

本文关键字:实例 删除 何手动      更新时间:2023-10-16

如何手动删除类的实例?

示例:

#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 << "nn";
    return 0;
}

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

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

当然,这可能需要实现赋值运算符,但按照三或五的规则,无论如何都应该这样做(但如果按照零的规则,则不需要赋值运算符)。

如果您明确希望销毁当前swiss对象:,则也可以使用指针

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.

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

很少有实例需要这样做。但在创建抽象数据类型时可能会遇到一个问题。

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

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

生动:

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

要清除:

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

然而,正如在许多其他帖子中提到的那样。如果您正在正常编程,那么您不需要担心手动调用dtor。如果它在堆栈上,那么它会为您清理。如果它在堆上,那么delete将为您处理它。唯一想这样做的时候是手动控制对象的生存期,而想要这样做的主要原因是在实现抽象数据类型时。

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

删除实例名称

  • 备注:不要在~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 << "nn";
    return 0;
}

C++的黑暗面提出了一种技术,可以准确地执行您在原始文章中描述的内容。我不知道你为什么要这样做——也许实现一个赋值操作符看起来很乏味。也许你有一种病态的欲望,想对你的程序做一些不自然的事情。我可以向你保证,没有一个"正常"的人会考虑使用我在下面透露的代码,至少在其他人观看的时候不会。那我为什么要把这个放在这里?

因为我是虐待狂。

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

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

如果由于任何原因不能使用赋值运算符,则可以使用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);

只要不存储可选项,就可以依靠编译器来优化多余的内部bool。