代码之家  ›  专栏  ›  技术社区  ›  Kevin Meredith

悬空指针示例

  •  6
  • Kevin Meredith  · 技术社区  · 14 年前

    在下面的代码中,为什么 s1.printVal 导致悬空指针错误?不是吗? s1 对象,即它的指针,在被销毁之前仍然可以访问?

    class Sample
    {
      public:
        int *ptr;
        Sample(int i)
        {
            ptr = new int(i);
        }
    
        ~Sample()
        {
            delete ptr;
        }
        void PrintVal()
        {
            cout << "The value is " << *ptr;
        }
    };
    
    void SomeFunc(Sample x)
    {
        cout << "Say i am in someFunc " << endl;
    }
    
    int main()
    {
        Sample s1 = 10;
        SomeFunc(s1);
        s1.PrintVal(); // dangling pointer
    }
    

    Source

    4 回复  |  直到 7 年前
        1
  •  15
  •   Nikolai Fetissov    14 年前

    这里的问题是 复制 这是为了 SomeFunc() . 该副本在销毁时会取消分配指针。您还需要实现 复制构造函数 复制分配运算符 . 见 rule of three .

    编辑:

    这里是“扩展的”伪代码,即编译器在 main() 功能:

    // main
    addr0 = grab_stack_space( sizeof( Sample )); // alloc stack space for s1
    Sample::ctor( addr0, 10 );                   // call ctor of Sample
    addr1 = grab_stack_space( sizeof( Sample )); // alloc stack for argument
    Sample::ctor( addr1, addr0 );                // call COPY-ctor of Sample
    SomeFunc( addr1 );                           // call SomeFunc
    Sample::dtor( addr1 );                       // XXX: destruct the copy
    free_stack_space( addr1, sizeof( Sample ));  // free stack taken by copy
    Sample::PrintVal( addr0 );                   // call member func on s1
    Sample::dtor( addr0 );                       // destruct s1
    free_stack_space( addr0, sizeof( Sample ));  // YYY: free stack taken by s1
    

    这是 不是准确的表达 但是概念上的解释。它只是有助于思考编译器与代码之间的关系。

    的指针成员 Sample delete -Ed在标记为 XXX 然后 删除 -又一次在台阶上 YYY .

        2
  •  3
  •   ereOn    14 年前

    尼古拉的回答解释了一切,但这里有一个可能的选择:

    如果您打算多个实例 Sample 要共享同一个基础指针,还可以使用 boost::shared_ptr 而不是原始指针。

    这有一点成本,但可能不会比你自己做的多。

    此外,这将避免编写任何复制构造函数、析构函数和赋值运算符。

        3
  •  2
  •   James Curran    14 年前

    当你打电话 SomeFunc(Sample x) 对象 x 是通过调用 Sample 的复制构造函数。由于您没有显式地编写一个,编译器会创建一个隐式的。通常情况下,隐式的一个很好:它执行一个成员一个成员的复制(如果您使用了 vector<int> 而不是 int* 你的代码会起作用的)。但是,在这种情况下,这是不好的。它只是复制了 ptr 现在,x.ptr&s1.ptr指向同一个int[]。现在,当某些功能结束时, X 是毁灭,毁灭者被调用,意思是 PTR 被删除。释放内存 X 但因为它的值相同,所以它也是 s1 .

        4
  •  1
  •   Seb Rose    14 年前

    尼古拉的回答是绝对正确的。就像爱伦的。

    您还需要考虑传递值和传递引用之间的差异。

    如果somefunc声明为:

    void SomeFunc(Sample& x)

    甚至更好

    void SomeFunc(const Sample& x)

    你不会有悬空指针的问题。

    你定义的方式 SomeFunc , the Sample 对象是按值传递的,这意味着在 某物 . 然后,什么时候 某物 返回临时对象超出范围并调用其析构函数,该函数将删除 ptr .

    如果您将引用传递给 样品 对象,则在调用时不会生成任何副本 某物 因此,当 某物 返回。