涉及类析构函数和删除运算符的一些内存管理问题

A few memory management questions involving class destructors and delete operator?

本文关键字:内存 管理 问题 运算符 析构函数 删除      更新时间:2023-10-16

在阅读了一些教程后,我仍然不清楚有关C++内存管理的一些要点。

1.当使用 new 运算符声明的类超出范围时,是否调用其析构函数并释放内存?是否有必要调用 delete 运算符来释放类的内存并调用其析构函数?

class Test{};
void newTest(){
    Test *t = new Test;
}
int main()
{
    newTest();
    return 0;
}

阿拉伯数字。当变量(如向量(所在的类被销毁时,是否释放了使用 new 关键字声明的变量(例如向量(?是否必须在类的析构函数中显式删除这些变量?

class Test{
    vector<int> *vec;
public:
    Test();
};
Test::Test(){
    *vec = new vector<int>;
}
void newTest(){
    Test t;
}
int main()
{
    newTest();
    return 0;
}

3.与问题 2 相同,但在堆栈上声明了正常变量。在以下示例中,当 t 超出范围时,矢量 vec 是否被删除?

class Test{
    vector<int> vec;
};
void newTest(){
    Test t;
}
int main()
{
    newTest();
    return 0;
}

4.最后,在类中声明向量的最佳方法是通常(在堆栈上(还是使用 new 关键字(在堆上(?

听起来你可能来自Java背景,所以new的事情在C++中的工作方式略有不同。

    使用
  1. new 分配的内存必须使用 delete 显式销毁。此示例存在内存泄漏。

  2. 与问题 1 类似,必须删除类析构函数中的内存才能回收它。

  3. 在这种情况下,矢量将自动销毁。

  4. 在类中,始终更喜欢按值存储成员(而不是按值存储成员(,除非您有其他特定原因。仅仅因为它在类中按值存储并不意味着它在堆栈上,它位于包含类占用的同一内存区域中。

最后请注意,在C++中,您可以使用RAII使内存管理不易出错。例如,看看shared_ptr、scoped_ptr和unique_ptr。这些都处理在适当的时候自动释放new分配的内存。

  1. 否,不调用析构函数,也不释放内存。 您必须delete对象。
  2. 否,变量未释放。您必须在析构函数中删除它们。
  3. 是的,vec 被删除(调用析构函数并释放内存(。
  4. 如果可能的话,通常在堆栈上更好。例如,如果将对象的所有权传递给另一个对象,则可能无法实现。此外,不应在堆栈上分配太多内存(操作系统对此有限制(,在这种情况下,应在堆上分配对象。
  1. 否,当指针超出范围时,使用 new 创建的对象不会自动销毁。您需要显式delete任何此类对象。
  2. 同样的答案。必须显式释放分配了new的任何内存。大多数编写良好的类都会在析构函数中为您执行此操作。如果要编写类,请确保清理类使用的任何内存。
  3. 此对象会自动为您销毁。而且,如果它写得很好,它会自动销毁它分配的任何内存。
  4. 大多数类实例应分配为局部变量,因为这将确保自动为您清理它们。但是,如果您需要全局或作为类的一部分,则需要适当地声明它们。

您应该更愿意说对象具有动态存储,而不是"在堆上"。同样,使用"自动存储"而不是"在堆栈上"。

前者实际上是在标准中使用的,而后者则更口语化。

如果对象具有自动存储,则编译器将在对象超出范围(在包含块的末尾(时自动调用其析构函数。

需要显式释放分配new的内存。将回收实际指针的存储,但不回收它指向的对象。

因此,如果您使用new则需要具有匹配的deletenew[]需要与delete[]匹配。

(一个非常有用的"智能指针",因为它将帮助您减轻跟踪newdelete的麻烦。

销毁向量(如果分配了 new 则通过删除,或者如果 auto,则通过 void(将调用其元素的析构函数。这就是为什么我们应该首选的方式来保存多个对象。

结论:

如果可能,请尝试使用智能指针或集合。否则,请确保您deletenew的内容。(如果你编写一个类,在构造函数中分配任何动态成员,并在类的析构函数中删除它们。

  1. 当使用 new 运算符声明的类超出范围时,是否调用其析构函数并释放内存?是否有必要调用 delete 运算符来释放类的内存并调用其析构函数?

首先是一些术语:

变量是对象或指针(clas 不是用新指针声明的(。
当对象超出范围时,它们会自动销毁。
指针不会自动删除。

第二种首选对象而不是指针

void newTest()
{
    Test t;  // t created here and automatically destroyed.
}

第三。如果必须创建指针。把它放在一个智能指针里。

void newTest()
{
    std::auto_ptr<Test> t(new Test());  // t created here and automatically destroyed.
                                        // Which will call delete on the contained pointer.
}

回答问题1:

您声明的指针超出范围,但未调用内存析构函数,并且内存泄漏。您需要手动执行此操作(或使用我指出的技术之一(。

  1. 当变量(如向量(所在的类被销毁时,是否释放了使用 new 关键字声明的变量(例如向量(?是否必须在类的析构函数中显式删除这些变量?

术语

你的意思是pointers initialized with new而不是declared with the new keyword

回答问题2。

不。必须在析构函数中手动调用销毁。但最好不要在类中设置指针。而是在类中声明一个向量对象,然后它将被自动销毁。

class Test
{
    vector<int>                   vectorObject;
    std::auto_ptr<vector<int> >   vectorPtrInSmartPointer;
    vector<int>*                  vectorRaw
public:
    Test();
    ~Test();
private:
    Test(Test const& copy);
    Test& operator=(Test const& copy);
};
Test::Test()
   : vectorPtrInSmartPointer(new vector<int>())  // Need to initialize the smart pointer.
   , vectorRaw(new vector<int>)                  // Need to initialize the RAW pointer
{
   // Note the vectorObject was automatically created.
}
Test::~Test()
{
   delete vectorRaw;                             // Need to manually release the RAW pointer.
}
// Smart pointer and object auto released.

注意: 由于类 Test 包含一个 RAW 指针(矢量*(,我不得不手动禁用复制构造函数Test::Test(Test const&)和赋值运算符Test& operator=(Test const&)。这是为了确保遵循 3 法则。

  1. 与问题 2 相同,但在堆栈上声明了正常变量。在以下示例中,当 t 超出范围时,矢量 vec 是否被删除?

是的。

  1. 最后,在类中声明向量的最佳方法是通常(在堆栈上(还是使用 new 关键字(在堆上(?

将其称为堆栈/堆会掩盖细节(因为成员可能是动态分配对象的自动成员(。宁愿将它们视为automatic variablesdynamic variables。当automatic variables的包含范围被破坏时,它们会自动销毁。因此,对于函数变量,这意味着当您离开函数时。对于类成员,这意味着当对象被销毁时。选择是更喜欢使用automatic variables(即不与新的分配(。

这两个简单的规则应该回答所有问题:

  1. 您应该在代码中具有尽可能多的delete new
  2. 尽可能从堆栈而不是堆中分配。