【问题标题】:How do I un-initialize an object in C++?如何在 C++ 中取消初始化对象?
【发布时间】:2015-05-13 22:30:01
【问题描述】:

我不是 100% 确定我为这个权利写了标题,所以这就是我想要做的......

我有一个这样定义的类...

class Animal
{
public:
    Animal() : m_name("New Animal")
    {
    }

    Animal(const std::string& name) : m_name(name)
    {
    }

    Animal(const Animal& animal) : m_name(animal.name)
    {
    }

private:
    const std::string name;
};

然后我像这样初始化这个类的一个对象......

Animal* m_animal = new Animal("Leroy");

在我的程序中的某个时刻,用户将单击一个按钮,这将导致 m_animal 变为空。这意味着名为 Leroy 的宠物不应该再存在了..

我以为我可以使用 delete m_animal,但是一旦我调用它,我就不能再使用 m_animal 而不会导致内存分配错误。

所以我想我的问题是...... 我使用以下代码会导致内存泄漏,因为名为 Leroy 的宠物没有被删除……如果是这样,有什么替代方法可以完成这项工作?

m_pet = NULL;

整个过程应该是这样的……

Animal* m_animal = new Animal("Leroy");
Animal* m_animal2 = new Animal("Boo");
std::cout << m_animal.name << endl;
m_animal = NULL;
m_animal = new Animal(m_animal2);
std::cout << m_animal.name << endl;

【问题讨论】:

  • 是的,这是内存泄漏
  • 使用m_animal-&gt;name 而不是m_animal.name,因为它是一个指针。

标签: c++ class object initialization


【解决方案1】:

确实想使用delete m_animal;。你可以这样做:

std::cout << m_animal.name << endl;
delete m_animal;
m_animal = new Animal(m_animal2);
std::cout << m_animal.name << endl;

调用delete m_animal后,你不能再使用m_animal指向的东西,但你当然可以再次使用m_animal指针。

【讨论】:

  • 您应该在删除后将 m_animal 设置为 nullptr/NULL,否则您可能会出现双重删除情况。 OP 可能应该使用 shared_ptr。
  • @James:我给出的代码没有问题;在这种情况下,m_animaldelete 之后立即分配了一个新值。但是,如果您不打算立即给指针一个新值,那么分配 NULL 会很有用。
  • 工作得很好,我现在唯一的问题是......所以在我的例子中,当我从 m_animal2 创建一个新动物时......如果我不再使用 m_animal2,我是否必须使用 delete m_animal2?
  • @GregHewgill 如果new 抛出,你可能会得到双重删除。
  • 在 OP 的示例代码中,没有 try 块,并且指针被声明在与 new/delete 相同的范围内。因此,异常处理不可能导致双重删除,因为当任何异常被捕获时,所讨论的指针已经超出了范围。话虽如此,是的,当然你们都是正确的,添加异常处理确实需要将额外的考虑因素应用于代码构造。同样,多线程和共享内存也是如此,另外两件事可能会使我的示例无效。但这些不是问题的一部分。
【解决方案2】:

您可以添加一个clear 成员函数(例如)将名称设置为空字符串。这意味着“Leroy”不再存在,但存在一个处于有效状态的Animal,因此您可以使用它来容纳其他动物,而无需删除旧的并分配新的。

然而,这仍然留下一个 Animal 对象——只是一个没有名字的对象。如果您想将分配与内存中对象的创建/销毁分开,您可以使用operator new 分配原始内存,然后使用placement new 在该内存中创建一个对象。当你想销毁对象时,可以直接调用它的析构函数,它会真正销毁对象(但保留分配的内存)。

当你用完内存后,你可以使用operator delete来删除内存。

除此之外:这几乎就是std::vector 的一个例子,它使用它的内存块。虽然在处理多个对象时它可能更多有用,但对单个对象执行此操作也没有什么特别的错误。

除了#2:在大多数情况下,您不需要(或真的想要)直接使用newdelete,如上所示。在相当多的情况下,您可以使用std::shared_ptrstd::unique_ptr 代替std::make_sharedstd::make_unique 来分配对象。

【讨论】:

  • 我的错 - 我没有正确阅读您的第 1 段......对于重复的答案感到抱歉。 +1 伟大的思想等等。
  • 但是如果你选择使用这个方法,请记住你不能在其他任何地方给出对象,因为将对象重复用于另一个动物也会改变指向其他地方的对象。因此,尤其是作为初学者,每次都使用新对象可能会更好。
【解决方案3】:

有时(但不是对于这个 mcve),使用 init() 方法更简单。

虽然我更喜欢您在此处使用的初始化列表(在 ctor 之后),但有时您会遇到系统启动序列选项,其中 ctor 参数 not 尚可用,因此不能由ctor填写。当两个或多个实例(相同或不同的类)具有指向另一个的指针(如在工作和保护硬件控制中)时,这是一个特殊的问题

因此,您可以考虑以下方法,它通过提供 init() 方法来解决排序和相互依赖的挑战。

class Animal
{
public:
    Animal()
    {
        init("New Animal")
    }

    Animal(const std::string name)
    {
        init(name);
    }

    Animal(const Animal& animal)
    {
        init(animal.m_name); 
    }

    void init(std::string name)
    {
       m_name.erase(); // clear the previous attribute 
       //           (not really needed here, but included for clarity)
       m_name = name;  // fill in new attribute 

       // and continue with both clear (when needed) and init's 
       // of all the other data attributes 
       // in an order similar to the initialization list.
       // note that the compiler won't be able to notify you
       // of out of order initialization issues.
   }

private:
    const std::string name;
};

那么,init() 是否适用于其他任何事情?

你问了

如何取消初始化对象。

在这种情况下,您可以简单地使用“init(...)”同时进行

a) 清除之前的状态信息(必要时)

b) 像新创建一样初始化状态信息

c) 避免其他方法相对昂贵的删除和新方法。

【讨论】:

    【解决方案4】:

    如果您使用new 运算符创建对象,则需要使用delete 运算符将其删除。

    Animal* animal = new Animal( "Pig" );
    
    // Using the animal ...
    
    delete animal;
    animal = nullptr;
    

    您需要使用new - delete 对以避免内存泄漏。

    【讨论】:

    • 所以在我的关于示例中,当我从 m_animal2 创建一个新动物时...如果我不再使用 m_animal2,我是否必须使用 delete m_animal2?
    • 是的,当你不想再使用时需要删除它。
    【解决方案5】:

    因为这两个是指向 Animal 类的指针。这也应该有效:

    std::cout << m_animal->name << endl;
    m_animal = m_animal2;
    std::cout << m_animal->name << endl;
    

    不需要为动物第三次分配内存,即使其中一个之前被删除,其代码效率低下。然后你应该只删除这些指针之一并将它们都设置为nullptr。不过,使用std::shared_ptr 可能是更可靠的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-28
      • 1970-01-01
      • 1970-01-01
      • 2016-12-17
      • 2013-07-06
      • 1970-01-01
      相关资源
      最近更新 更多