【问题标题】:Confused with delete keyword operation in C++对 C++ 中的删除关键字操作感到困惑
【发布时间】:2013-04-29 12:35:52
【问题描述】:

我想知道删除是如何工作的? 在主要功能中,我删除了cfactobject。但是cfact->Hello() 仍然有效,而不是引发错误。 在删除发生时我发现调试时,cfact 释放内存。只要factory* c2fact = newfun.Newfun("c2_fact"); 行执行cfact 就会获得一些内存位置。

class factory{

public:
    virtual void Hello() = 0;
};
class c_fact: public factory
{
public:
    void Hello(){
    cout << "class c_fact: public factory"<<endl;
    }
};
class c2_fact: public factory
{
public:
    void Hello(){
    cout << "class c2_fact: public factory"<<endl;
    }
};

class callFun{
public:
    virtual factory* Newfun(string data)
    {
        if(data == "c_fact")
            {return new c_fact;}
        else
            {return new c2_fact;}
    }
};
class newFun:public callFun{
public:
    factory* Newfun(string data)
    {
        if(data == "c_fact")
            {return new c_fact;}
        else if (data == "c2_fact")
            {return new c2_fact;}
    }
};
int main()
{
    newFun newfun;
    factory* cfact = newfun.Newfun("c_fact");
    delete cfact;                              //Deleted the instance
    factory* c2fact = newfun.Newfun("c2_fact");
    cfact->Hello();//Still it prints the output
    c2fact->Hello();
    system("pause");
    return 0;
}

【问题讨论】:

  • 您预计会发生什么?您正在对已删除对象调用函数。这是未定义的行为,任何事情都可能发生,包括打印输出。
  • @PeterWood:我希望cfact-&gt;Hello(); 失败。无意间看到了这种行为。所以,我无法理解我想问的问题。
  • 你应该使用虚拟析构函数

标签: c++ new-operator factory-pattern virtual-functions delete-operator


【解决方案1】:

delete 实际上并没有使其指向的内容无效。它只是告诉操作系统该内存可用于其他用途并且程序不再需要它。

如果它没有被其他数据覆盖,您的数据仍将保留在内存中,并且仍可访问。这是许多在开发阶段未被发现并随后出现的错误的原因。

现在有效的事实并不意味着它将永远有效。例如,如果您将代码移动到另一台机器或重新启动计算机,代码可能会出现段错误。

delete 之后设置指向NULL 的指针始终是一个好习惯。或者更好地使用smart pointers

【讨论】:

  • delete 可能会使它指向的内容无效,它只是不必根据标准。这是一个实现细节。
  • 同样delete不会将内存返回给操作系统,它只会将内存返回给应用程序内存池,这可能不一样。这也是一个实现细节。但是,如果 delete 在正常的用户空间环境中直接通过操作系统内存处理,那将是一个糟糕的实现。
【解决方案2】:

这是未定义的行为,很可能是因为 Hello 方法没有使用任何类变量,因此没有使用 this 指针。尝试在Hello 中输出this,您应该会在调用delete 后看到一个无效指针:

std::cout << std::hex << this << << std::endl ;

在我的测试用例中,它在delete 之后返回为0

【讨论】:

    【解决方案3】:

    取消引用已删除的指针是未定义的行为。这意味着任何事情都可能发生,包括看起来“工作”的程序。您不能依赖任何此类行为。

    【讨论】:

      【解决方案4】:

      当您删除内存时,它会被释放。但是,内容通常不会更改,因此删除后写入该内存中的任何内容仍然存在,但是您不知道它将保留多长时间,因为其他功能可以抓取它并用自己的数据覆盖它。

      在某些编译器上,在调试模式下编译时,内存会被标记,以便您可以通过重用已删除的指针来检测此类错误。但是,这不一定是默认设置。所以你永远不应该重用一个被删除的指针。

      【讨论】:

        【解决方案5】:

        对不起,我不能评论... 我编译了你的代码,你可以观察到 c2fact 替换了你刚刚销毁的 cfact (输出是

        类 c2_fact:公共工厂

        c2_fact 类:公共工厂 )

        顺便说一句,如果你输入“cfact->Hello();”在您创建 c2fact 之前,程序可能会崩溃(这似乎是您所希望的),因为 mem 块不受任何对象的影响。请注意,此行为可能会根据内存监控和其他正在运行的进程而改变。

        【讨论】:

        • 他们的关键字是“......可能会崩溃......”。由于这个程序很短而且做的不多,所以它很可能会被忽视。当然,它依赖于编译器,甚至依赖于库。它可能会立即崩溃,或者它可以在生产代码中工作多年,让您的代码在完全不同的地方崩溃。
        • Arf 我不能赞成你的评论......事实上,也许我应该纠正自己:“如果你在创建 c2fact 之前输入“cfact->Hello();”,程序将碰撞”。我刚刚使用 Win7 对其进行了测试;) 和 mingw, anuone 使用其他操作系统/编译器对其进行测试?
        • 刚刚用 gcc 3.4 试过了。在 Eclipse 下在 Windows XP 上使用 cygwin,它不会崩溃。编译器选项也很重要。如果你放 -g 它可能会表现不同然后使用 .i.e -O2 或其他。我也尝试了您建议的更改,但它不会崩溃。 :)
        • 感谢您的澄清...现在我想尝试这些我以前从未听说过的编译器选项!
        • -g 是在二进制文件中包含调试信息,因此调试器会为您提供源代码和变量名称。 -O2 是一个优化级别,因此编译器会尽可能优化代码。但是不建议同时使用 -g 和 -O
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多