【问题标题】:could you tell me why does this code crash?你能告诉我为什么这段代码会崩溃吗?
【发布时间】:2012-04-04 21:16:36
【问题描述】:

所以我很好奇以下代码崩溃的原因。 将不胜感激。

#include <iostream>
using namespace std;

class temp
{    
  public:

    temp(int i)
    {
        intPtr = new int(i);
    }

    ~temp()
    {
        delete intPtr;
    }

  private:
    int* intPtr;
};

void f (temp fInput)
{
    cout << "f called" << endl;
}

int main()
{
    temp x = 2;
    f(x);
    return 0;
}

【问题讨论】:

  • @Ed S:我同意,但是,有时我们确定的崩溃可能与他认为的崩溃不同。
  • @josephthomas:是的,我也想这么多,这就是为什么我在发布后不久就删除了我的评论 :) 我们可以就该术语的正确使用达成一致......无论大多数初学者是否会使用这是另一个问题
  • 使用new需要正确管理内存。或者你可以不使用new 而是使用整数而不是指向整数的指针。

标签: c++ class function pointers crash


【解决方案1】:

你违反了The Rule of Three

您维护一个指针成员并将对象的副本传递给函数f。所以,最终结果是你在同一个指针上调用了两次delete

【讨论】:

    【解决方案2】:

    由于您传递x 的方式而发生崩溃。

    f函数作用域之后,会调用x的解构并删除intPtr

    但是,这将删除仍在main 范围内的内存。因此,在调用return 0 之后,它会尝试删除已经存在的内存,因为您在同一个指针上调用了两次 delete。

    要修复此错误,请更改

    void f (temp fInput)
    

    void f (const temp& fInput)
    

    或者,您可以考虑使用std::shared_ptr

    【讨论】:

    • 添加复制构造函数和赋值运算符也是一个好主意。您还可以使用 std::shared_ptr 代替原始指针,因为它负责复制语义。
    • 我同意,我想让发帖者知道一个可以帮助他以最小的努力的解决方案。有很多方法可以解决这个崩溃。
    【解决方案3】:

    在传递x(隐式复制构造函数)时复制指针并调用两次析构函数(在函数返回之前和main返回之前),因此内存被删除了两次。

    在此处使用 std::shared_ptr&lt;int&gt; 而不是原始 int 指针(假设您希望行为相同,即从两个 temps 引用相同的 int;否则,实现复制构造函数,移动构造函数和赋值运算符自己)。

    #include <memory>
    
    class temp {    
    public:
      temp(int i) : intPtr(std::make_shared<int>(i)) {
    
      }
    
    private:
      std::shared_ptr<int> intPtr; // reference counting ftw
    };
    

    【讨论】:

    • 或者只是正确地实现类开头(即正确的复制语义等)
    • 这也是可能的,这取决于你想要的行为。
    • OP 没有提到哪种行为是“正确的”,所以我不能神奇地知道。
    • 他不需要。鉴于类的定义,不提供复制构造函数并遵循三规则是不正确的。
    • 对于“行为”,我的意思是“复制 int 还是复制指向 int 的指针?”使用 shared_ptr 你会得到后者,你仍然可以断言编译器会为你生成一个复制构造函数。
    【解决方案4】:

    您在这里遇到的问题是双重删除。因为您没有在这里定义任何复制构造函数,所以 C++ 很乐意为您这样做。该实现通过执行所有内容的浅拷贝来实现这一点。

    f(x);  
    

    此行通过创建x 的副本并将其传递给f 来工作。此时有 2 个 temp 实例拥有一个 intPtr 成员。这两个实例都会删除该指针,这很可能是导致您崩溃的原因。

    要解决这个问题,您可以采取一些步骤

    1. 使用用于共享的指针类型,例如shared_ptr&lt;T&gt;
    2. 创建一个不可调用的复制构造函数,强制实例通过 ref 传递

    #2 的一个例子是

    class temp {
      temp(const temp& other);
      temp& operator=(const temp& other);
    public:
      // Same
    };
    

    现在f(x) 行根本无法编译,因为它无法访问必要的复制构造函数。它强制它改为重新定义 f 以防止复制。

    void f(const temp& fInput) {
      ...
    }
    

    【讨论】: