了解复制构造函数

Understanding Copy Constructor

本文关键字:构造函数 复制 了解      更新时间:2023-10-16

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

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;
}

我预计会抛出一些异常,因为我还没有明确声明我的复制构造函数,而且我也在使用指针。但程序运行得很完美。有人能提出修改建议吗?这样我就能理解在什么情况下会发生异常?

您的代码对同一指针执行delete两次。这是不明确的行为,以及未定义的行为是,它似乎可以工作。(在我的经验,最常见的症状是一切正常直到你把它呈现给公众,在这个时候,它开始左右碰撞。)

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

给定以下代码:

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_1pointer_2的值。只有物体不在了。在C或C++标准中,没有任何规定在删除内存或更改指针时必须通知程序。

没有任何内容表明,当删除内存时,实现必须更改指向已删除内存的每个指针。

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

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

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

我告诉你和你的朋友指着地板上的一块地毯。我把地毯拿掉。你和你的朋友在指什么?

类似的东西

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

但是delete应该只为程序的其他部分腾出内存,而不一定是null

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

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

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

现在,如果您销毁对象-内存"0xfeedfac"将未分配&回到您的分配器免费列表。想想对象aa发生了什么。它仍然保存着对0xfeedface的引用,但该内存已经被释放!现在,如果对象aa试图取消引用*p,它可以获得潜在的随机值(取决于分配器的操作,或者对象是否已分配给其他对象)。此外,如果对象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;
}

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

[注意:上面的解决方案不能很好地用于多个继承级别]