【问题标题】:c++ destructor doesn't delete the object itself, what does?c ++析构函数不会删除对象本身,是什么?
【发布时间】:2021-11-05 10:20:55
【问题描述】:

首先,除非我误解了它,否则析构函数会释放对象内部变量占用的内存。在下面的示例中,它将删除 * str 指向的字符串。就是这样。

class StrPtr {
private:
    string * str;

public:
    StrPtr() : str(new string()) {} 
    
    ~StrPtr() { delete str; }
    void add(char c) { str->push_back(c); }
};

但是,这是我的困惑。如果析构函数不破坏对象本身。这是否意味着:

  1. 在 for 循环之后,我有 10k 个 StrPtr 对象。

  2. for 循环一结束,10k StrPtr 对象就会被删除,因为它们超出了范围。

  3. 每次 someF() 函数完成执行时,对象都会被删除,因为它超出了范围。因为这是创建它的地方。这是我目前的想法。

    void someF() {
        StrPtr s;
        s.add('a');
    }
    
    int main() {
        for (int i = 0; i < 10000; i++) {
            someF();
        }
    
    }

【问题讨论】:

  • 不删除就会泄露。
  • 你的循环将进入someF 10000 次,每次它创建一个StrPtr,添加'a',然后在函数返回主循环时销毁StrPtr。在此代码中,您一次最多有 1 个StrPtr。编辑:这是你的情况#3。
  • 这里没有泄漏。但是StrPtr 不遵守 5/3/0 规则(复制/移动构造函数/赋值)。
  • 在类的构造函数中使用简单的std::cout &lt;&lt; "ctor\n";,在类的析构函数中使用std::cout &lt;&lt; "dtor\n";,可以消除大部分/所有困惑。

标签: c++ memory-leaks scope destructor


【解决方案1】:

请参阅我的问题的选项 3。

没有内存泄漏,内存中的 StrPtr 对象永远不会超过一个。

【讨论】:

    【解决方案2】:

    我想你只需要了解“删除”、“解除分配”和“销毁”之间的区别。可以帮助你的是观察

    {
        A *a = new A;
        ...
        delete a;
    }
    

    实际上是这样工作的:

    {
        A *a = static_cast<A *>(std::malloc(sizeof(A)); // allocate
        new (a) A(); // construct [placement new, something like a->A()]
        ...
        a->~A(); // destroy
        std::free(a); // deallocate
    }
    

    所以,“删除”意味着“销毁”,然后是“解除分配”。 “创建”分别表示“分配”,然后是“构造”。

    在这个方案中, (std::malloc, std::free) 可以用任何其他分配策略代替——看看https://en.cppreference.com/w/cpp/memory/allocator_traits。 这同样适用于堆栈上的分配。

    {
        A a;
        B b;
        ...
    }
    

    可能看起来像这样:

    {
        // push %rbp
        // mov  %rsp, %rbp
        A *pa = <magic> // sub $sizeof(A), %rsp; pa <- %rsp
        B *pb = <magic> // sub $sizeof(B), %rsp; pb <- %rsp
        A &a = *pa;
        B &b = *pb;
        ...
        pb->~B();
        pa->~A();
        <deallocate everything>
        // mov  %rbp, %rsp
        // pop  %rbp
    }
    

    如果您想了解堆栈分配的魔力,最好学习汇编的基础知识——您还将了解调用帧、调用约定和许多其他您通常必须记住而不是推导的东西.

    【讨论】:

    • 感谢您指出这一点。我隐约知道调用框架、堆栈、堆和调用约定,我确实在 arduino 上做了一些组装。您的回答虽然内容丰富,但可能有点过于复杂。我从其他回复中掌握了窍门。
    猜你喜欢
    • 2012-04-30
    • 2022-08-16
    • 2021-04-10
    • 2017-05-18
    • 2013-09-30
    • 2013-01-16
    • 2013-05-21
    • 2019-01-29
    • 1970-01-01
    相关资源
    最近更新 更多