在哪种情况下删除指针

时间:2015-09-25 10:42:29

标签: c++

我的以下问题是内存管理。我有一个int变量没有在类中动态分配,比如说invar1。我将此int的内存地址传递给另一个类构造函数。那个班级做到了这一点:

List<T>

我应该删除ptoint吗?因为它具有动态分配的int的地址,所以我认为我不需要删除它。

我再次向具有new运算符的类声明一个对象:

class ex1{
    ex1(int* p_intvar1)
    {
       ptoint = p_intvar1;
    }

    int* ptoint;
};

我将其传递给另一个班级:

objtoclass = new ex1();

当我删除objtoclass时,我应该删除obj吗?

谢谢!

5 个答案:

答案 0 :(得分:12)

  

因为它具有动态分配的int的地址,我认为我不需要删除它。

正确。

  

当我已经删除了objtoclass时,我应该删除obj吗?

没有

回想一下,你实际上并没有删除指针;你正在使用指针删除他们指向的东西。因此,如果您同时写了delete objdelete objtoclass,因为两个指针都指向同一个对象,您将两次删除该对象。

我会提醒你,这对于你的ex2类来说是一个非常容易犯的错误,在这个类中,该指向对象的所有权语义并不完全清楚。您可以考虑使用智能指针实现来消除风险。

答案 1 :(得分:8)

只是其他答案的附录

你可以借助智能指针(<? require_once("constructor.php"); $smarty->display("page_index"); if (isset($_GET["page"])) { $page = htmlspecialchars($_GET["page"]); $setPageFile = "module/".$page.".php"; if (file_exists($setPageFile)) { include($setPageFile); } else { $smarty->assign("alert", array(1, "Żądany moduł nie jest obsługiwany.")); } } else { Header("Location: login"); } $smarty->display("page_footer"); ?> shared_ptr)摆脱原始指针并忘记内存管理。

智能指针负责在内存超出范围时释放内存。

以下是一个例子:

unique_ptr

输出:

#include <iostream>
#include <memory>

class ex1{
public:
    ex1(std::shared_ptr<int> p_intvar1)
    {
        ptoint = p_intvar1;
        std::cout << __func__ << std::endl;
    }

    ~ex1()
    {
        std::cout << __func__ << std::endl;
    }
private:
    std::shared_ptr<int> ptoint;
};

int main()
{
    std::shared_ptr<int> pi(new int(42));
    std::shared_ptr<ex1> objtoclass(new ex1(pi));

    /* 
     * when the main function returns, these smart pointers will go
     * go out of scope and delete the dynamically allocated memory
     */ 

    return 0;
}

答案 2 :(得分:4)

  

当我删除objtoclass时,我应该删除obj吗?

嗯,你可以记住,两次删除同一个对象是未定义的行为,应该避免。例如,如果您有两个指针,例如指向同一个对象,并且您使用一个指针删除原始对象,则可能会发生这种情况 - 那么您也不应该使用另一个指针删除该内存。在你的情况下,你可能最终得到两个指向同一个对象的指针。

一般来说,构建一个内部管理内存的类(就像你看似的那样)并不是一件容易的事情,你必须考虑像rule of three之类的东西。

关于那个应该删除动态分配的内存是对的。如果没有动态分配内存,则不应删除内存。

PS。为了避免上述复杂情况,您可以使用智能指针。

答案 3 :(得分:2)

您目前不会删除此int,或显示其分配位置。如果两个对象都不应该拥有它的参数,我就写

struct ex1 {
    ex1(int &i_) : i(i_) {}
    int &i;               // reference implies no ownership
};
struct ex2 {
    ex2(ex1 &e_) : e(e_) {}
    ex1 &e;               // reference implies no ownership
};

int i = 42;
ex1 a(i);
ex2 b(a);

如果新对象认为任何一个参数是拥有,则将其作为unique_ptr传递。如果任一参数应该共享,请使用shared_ptr。我通常更喜欢这些(引用或智能指针)中的原始指针,因为它们提供了有关您的意图的更多信息。

一般来说,做出这些决定,

  

我应该删除ptoint吗?

是错误的问题。首先要考虑稍微高一些的事情:

  1. 这个int在你的程序中代表什么?
  2. 谁,如果有人拥有它?
  3. 与使用它的这些类相比,它应该存在多长时间?
  4. 然后看看这些例子的答案是如何自然消失的:

    • 此int是I / O映射控制寄存器。

      在这种情况下,它不是用new创建的(它存在于整个程序之外),因此你当然不应该删除它。它也应该标记为volatile,但这并不会影响生命周期。

      也许类外的东西映射了地址,也应该取消映射,这与(de)分配它很相似,或者它可能只是一个众所周知的地址。

    • 此int是全局日志记录级别。

      在这种情况下,它可能具有静态生命周期,在这种情况下,没有人拥有它,它没有明确分配,因此不应该明确地取消分配

      或者,它由logger对象/ singleton / mock / what拥有,并且该对象负责在必要时解除分配

    • 这个int被明确地赋予你的对象

      在这种情况下,明智的做法是好的,例如。

      ex1::ex1(std::unique_ptr<int> &&p) : m_p(std::move(p)) {}
      

      请注意,将您的本地数据成员设为unique_ptr或类似,也会自动处理生命周期,而您无需付出任何努力。

    • 这个int被赋予你的对象 use ,但是其他对象也可能正在使用它,并且它们完成的顺序并不明显。

      使用shared_ptr<int>代替unique_ptr来描述此关系。同样,智能指针将为您管理生命周期。

    通常,如果您可以在类型中对所有权和生命周期信息进行编码,则无需记住手动分配和取消分配内容的位置。这更清晰,更安全。

    如果您无法对该类别中的信息进行编码,您至少可以清楚自己的意图:您在不提及生命或所有权的情况下询问释放的事实,建议您&# 39;在错误的抽象层面上工作。

答案 4 :(得分:2)

  

因为它具有动态分配的int的地址,我   以为我不需要删除它。

这是正确的。只是不要删除它。

问题的第二部分是关于动态分配的内存。在这里,你必须多思考一下并做出一些决定。

让我们假设你的名为 ex1 的类在其构造函数中接收一个原始指针,用于在类外部动态分配的内存。

作为该类的设计者,您必须决定此构造函数“是否取得此指针的所有权”。如果确实如此,则 ex1 负责删除其内存,您应该在类析构函数中执行此操作:

class ex1 {
public:
    /**
     * Warning: This constructor takes the ownership of p_intvar1,
     * which means you must not delete it somewhere else.
     */
    ex1(int* p_intvar1)
    {
        ptoint = p_intvar1;
    }

    ~ex1()
    {
        delete ptoint;
    }

    int* ptoint;
};

然而,这通常是一个糟糕的设计决定。你必须root用于这个类的用户阅读构造函数的注释,并记住不要删除在类 ex1 之外的某个地方分配的内存。

接收指针并获取其所有权的方法(或构造函数)称为“sink”。

有人会像这样使用这个类:

int* myInteger = new int(1);
ex1 obj(myInteger); // sink: obj takes the ownership of myInteger
// never delete myInteger outside ex1

另一种方法是说你的类 ex1 不占用所有权,并且为该指针分配内存的人负责删除它。类 ex1 不能删除其析构函数上的任何内容,它应该像这样使用:

int* myInteger = new int(1);
ex1 obj(myInteger);
// use obj here
delete myInteger; // remeber to delete myInteger

同样,您班级的用户必须阅读一些文档才能知道他有责任删除这些内容。

如果不使用现代C ++,则必须在这两个设计决策中做出选择。

在现代C ++(C ++ 11和14)中,您可以在代码中明确表示事物(即,不必仅依赖于代码文档)。

首先,在现代C ++中,您避免使用原始指针。您必须在两种“智能指针”之间进行选择:unique_ptr或shared_ptr。它们之间的区别在于所有权。

正如他们的名字所说,一个唯一的指针只由一个人拥有,而共享指针可以由一个或多个拥有(所有权是共享的)。

无法复制唯一指针(std :: unique_ptr),只能从一个地方“移动”到另一个地方。如果一个类具有唯一的指针作为属性,则显式该类具有该指针的所有权。如果一个方法接收一个唯一的指针作为副本,那么显然它是一个“接收器”方法(取得指针的所有权)。

您的课程 ex1 可以这样写:

class ex1 {
public:
    ex1(std::unique_ptr<int> p_intvar1)
    {
        ptoint = std::move(p_intvar1);
    }

    std::unique_ptr<int> ptoint;
};

此类的用户应使用它:

auto myInteger = std::make_unique<int>(1);
ex1 obj(std::move(myInteger)); // sink
// here, myInteger is nullptr (it was moved to ex1 constructor)

如果您忘记在上面的代码中执行“std :: move”,编译器将生成一个错误,告诉您unique_ptr不可复制。

另请注意,您永远不必显式删除内存。智能指针可以为您处理。