0

我已经读过,如果我们的类中有指针,那么我们需要实现自己的复制构造函数;否则,两个类将具有指向相同内存位置的指针,并且在其中一个上调用 delete 也会使另一个为空。我试图通过编写如下代码来模拟上述情况:

class A
{
    private:
        int *p;
    public:
        A()
        {
            p = new int(10);
        }

        ~A()
        {
            delete p;
            cout << "Calling destructor" << endl;
        }
};

int main(int argc, char **argv)
{
    A a;
    A aa = a;
}

我预计会引发一些异常,因为我没有明确声明我的复制构造函数并且我也在使用指针。但程序运行完美。任何人都可以提出修改建议,以便我能够理解在什么情况下会发生异常?

4

4 回答 4

5

您的代码delete两次执行相同的指针。这是未定义的行为,未定义行为的可能症状之一是它似乎可以工作。(根据我的经验,最常见的症状是一切正常,直到您将其展示给公众,此时,它开始左右崩溃。)

于 2013-08-06T18:48:04.007 回答
1

您的问题与有两个指针指向一个实例并删除该实例相同。

给定以下代码:

int main(void)
{
    int * pointer_1 = NULL;
    int * pointer_2 = NULL;

    pointer_1 = new int;
    *pointer_1 = 42;

    // Make both pointers point to the same dynamically allocated object.
    pointer_2 = pointer_1;

    // Let's delete the instance
    delete pointer_1;

    // The delete operator does not change the value of pointer_1.
    // Pointer_1 still points to *something*, but that *something* has been deleted.

    return 0;
}

在上面的例子中,delete不影响pointer_1or的值pointer_2。只有对象不再存在。C 或 C++ 标准中没有规定必须在删除内存或更改指针时通知程序。

没有什么说明当内存被删除时,实现必须更改每个指向已删除内存的指针。

因为对象不再存在,所以引用指针将产生未定义的行为。内容可能在内存中有影子,或者操作系统可能已经从内存中完全删除了页面(也称为内存分页)。

操作系统可能会抛出异常,但 C 和 C++ 语言不会强制编译器库生成异常。毕竟,在某些嵌入式系统中,地址 0 是有效的内存位置。

尝试在每一步打印指针的值以进行验证。

我告诉你和你的朋友指着地板上的地毯。我移开地毯。你和你的朋友指的是什么?

于 2013-08-06T19:53:15.077 回答
0

像这样的东西

A * a = new A();
A * b = a;
delete a;
stdout << (*(b->p));

delete应该只让内存自由供程序的其他部分使用,不一定是null它。

于 2013-08-06T18:48:43.387 回答
0

我认为这不会引发“异常”。您可能会得到不良副作用,例如 SEGV 或更严重的内存损坏。要理解为什么需要复制构造函数,您可以考虑对象 'a' 和 'aa' 的外观。

假设,new int(10) 返回一个指针值 0xfeedface,其中 *(int *)(0xfeedface) == 10。你的对象看起来像,

a -> { p=0xfeedface} aa -> { p=0xfeedface}

现在,如果您破坏对象-a,内存 '0xfeedfac'e 将被未分配并返回您的分配器空闲列表。考虑一下 object-aa 会发生什么。它仍然持有对 0xfeedface 的引用,但是那个内存已经被释放了!现在,如果 object-aa 尝试取消引用 *p 它可能会获得随机值(取决于分配器的作用或对象是否已分配给其他对象)。此外,如果 object-aa 尝试写入 p=0xfeedface,可能会发生可怕的事情。

如果你想强制编写一个复制构造函数,我能想到的一种方法是创建一个基类并断言它是否被调用。

#include <iostream>
#include <cassert>

class base
{
    public:
        virtual void operator=(const base& )
        {   
            assert(! "No copy constructor for class derived from base");
        }   
};

class derived : public base
{};

int
main()
{
   derived d, d1; 
   d1 = d;
}

上面的代码将断言,因为派生类没有提供复制构造函数。最好在编译时捕获它。但我现在想不出办法。

[警告:上述解决方案不适用于多级继承]

于 2013-08-06T19:17:20.173 回答