【问题标题】:Is it mandatory to delete a pointer variable within a structure, before deleting the structure?在删除结构之前是否必须删除结构中的指针变量?
【发布时间】:2019-02-01 14:50:42
【问题描述】:

我已经开始学习 C++,需要对 C++ 中的内存管理进行一些说明。我遇到过智能指针,但我想了解一些基本概念。

这是一个示例结构

struct A
{
    private:
    int a;
    void* b;
    public:
    A(int i, void* m) { a=i; b=m; }
};

main()
{
    A * a1 = new A(10, 0);
    // 
    //Some Code
    if(on some condition) {
         delete a1;
         a1=nullptr;
    }
}

当我删除 a1 时,m 是否也会被自动删除,或者我应该在删除 a1 之前明确删除 m,如下所示?

delete a1->b;
a1->b = nullptr;
delete a1;
a1=nullptr;

【问题讨论】:

  • 我遇到过智能指针我没有看到任何smart pointers。但是,如果您使用它们,则不需要 Basile 在他的回答中提到的显式析构函数
  • 一些注意事项:避免在 C++ 中使用 void*,因为几乎总有更好的方法(值得注意的例外:需要 void * 的旧接口)。 void* 有很多方法,最常见的可能是模板和继承。 C++ 最重要的习惯用法之一是Resource Allocation Is Initialization。观察 RAII 可以消除许多常见的错误系列。
  • 您并没有真正删除指针,而是删除了它们指向的对象。如果你有很多指向同一个对象的指针,你需要小心,这样你只删除一次对象。
  • 智能指针被称为是有原因的。普通指针是愚蠢的,它们从不删除自己,你必须以一种或另一种方式这样做。
  • A中没有数据成员m,你不能delete a1->m。你也不能在A 之外delete a1->b,因为b 是私有的。

标签: c++ pointers


【解决方案1】:

欢迎使用 C++,这是一种非常强大的语言,它要求您对细节负责,以实现允许这种功能的灵活性。如果你愿意,你可以让它变得非常复杂,但是对于大多数构造它们也是一种简单的方法。

首先,你不需要释放内存,如果你的程序退出,它会清理它。但是,由于您不调用 delete,因此不会调用 Dtor,这可能会导致特定代码无法执行。 所以一般来说,清理分配的内存是个好习惯。

如果你不需要堆,就不要使用它

new A(10, 0) 将在堆上分配内存。如果您不希望这样,也可以在堆栈上创建它。这会导致自动清理:A a{10, nullptr};

使用 RAII

一旦你决定,你需要堆分配内存,你应该默认为std::unique_ptr。将代码更改为:auto a = std::make_unique<A>(10, nullptr); 这样,所有权就在 unique_ptr 中,可以移动(std::move)。如果不想转让所有权,可以取消引用或者调用get方法。

应用这 2 种做法(包括针对成员)将防止大量内存泄漏并减少您需要考虑的时间。

不要使用 void*

void* 是邪恶的,除非必须,否则不要使用它(并且只有在与 C 交互时才需要)。有很多方法可以避免它。最好的是引入一个界面。

class I {
public:
    virtual ~I() = default;
};

class M : public I
{
    // ...
};

class A
 {
      // ...
      std::unique_ptr<I> m;
      // ...
  };

需要一些特别的东西吗?

有时,您需要在 Dtor 中添加一些特殊的东西,只有在这种情况下,您才应该显式地实现 Dtor。鉴于您的问题,我假设您是初学者,因此暂时不需要了解更多细节。

【讨论】:

  • 有哪些特殊细节可以添加到析构函数中,能否提供链接?
  • @vsk,“一些特别”的例子是 1) std::ofstream 析构函数刷新内部缓冲区,并关闭文件; 2)std::lock_guard析构函数自动解锁关联的互斥锁; 3)std::map删除平衡二叉树中的所有节点,同时调用所有键和数据的析构函数。还有很多例子
【解决方案2】:

当我删除a1时,m也会被自动删除

不,它不会(您的代码可能存在内存泄漏)。你需要一个明确的destructor 删除它。

顺便说一句,使用void*b; 指针字段的品味很差。如果你知道的话,你应该更喜欢一些更明确的类型(例如double*b;SomeClass* b;)。这使您的代码更具可读性,并为在编译时进行有用的类型检查提供了更多机会。

// inside struct A
~A() { delete b; };

阅读rule of five

请注意,struct-s 在 C++ 中是 very similarclass-es。

避免memory leaksvalgrind 之类的工具可能会有所帮助。并且系统地使用smart pointers 和标准容器应该有助于避免它们。如果您的字段b was 声明std::shared_ptr&lt;std::string&gt; b;,则默认析构函数会适当地释放它。也许您希望它是一些std::vector&lt;std::string&gt;(同样,默认析构函数正在适当地释放内存)。

一个好的编码提示是尽可能避免声明raw pointers(更喜欢智能指针和容器)。当你必须声明一个时,你需要适当地编码它的delete

【讨论】:

  • 我认为无法从发布的代码中判断是否存在内存泄漏。这将取决于该指针是否被应用程序中的任何其他源引用。 (传递 0 的示例对确定这一点没有帮助。)拥有不属于 struct 或使用它们的 class 的对象的弱指针是很常见的。
猜你喜欢
  • 2015-03-08
  • 2012-04-22
  • 1970-01-01
  • 2013-12-06
  • 2017-05-30
  • 1970-01-01
  • 1970-01-01
  • 2013-11-02
  • 1970-01-01
相关资源
最近更新 更多