C++使用指针复制构造函数

C++ copy constructor with pointers

本文关键字:复制 构造函数 指针 C++      更新时间:2023-10-16

我理解需要深度复制指针(在您想要对象的完整副本的情况下),我的困惑来自以下内容(完全编造的示例)。

#include "stdafx.h"
#include <string>
class a
{
public:
    a::a(std::string _sz) : 
        m_sz(_sz)
        ,m_piRandom(new int)
    {
        *m_piRandom = 1;
    };
    ~a()
    {
        delete m_piRandom;
        m_piRandom = NULL;
    };
    a::a(const a &toCopy)
    {
        operator=(toCopy);
    }
    a& a::operator=(const a &toAssign)
    {
        if (this != &toAssign)
        {
            m_sz = toAssign.m_sz;
            if (m_piRandom)
            {
                // Need to free this memory! 
                delete m_piRandom;
                m_piRandom = NULL;
            }
            m_piRandom = new int(*toAssign.m_piRandom);
        }
        return *this;
    }
    void SetInt(int i)
    {
        if (!m_piRandom)
        {
            m_piRandom = new int;
        }
        *m_piRandom = i;
    }
private:
    std::string m_sz;
    int* m_piRandom;
};

int _tmain(int argc, _TCHAR* argv[])
{
    a Orig = a("Original");
    a New = a("New"); 
    New.SetInt(9);
    New = Orig;
    return 0;
}

现在在我的示例中,我想测试一个场景,其中我有一个对象分配了一些内存,在本例中:

a New = a("New"); 
New.SetInt(9); // This new's the memory

分配内存,然后当我们说:New = Orig;我预计内存泄漏,因为如果我盲目地新m_piRandom = new int(*toAssign.m_piRandom);,我会失去它以前指向的内存。

所以我决定在赋值运算符中放入以下内容:

if (m_piRandom)
            {
                // Need to free this memory! 
                delete m_piRandom;
                m_piRandom = NULL;
            }

当调用以下内容(第一行! a Orig = a("Original");因为它调用复制构造函数(我调用赋值运算符以减少重复),并且指针m_piRandom设置为 0xcccccccc。不为空。因此,它会尝试删除从未分配的内存。我希望它在到达New = Orig;时能够工作,因为它会在分配副本之前先删除它。任何人都可以对此有所了解,我想我最大的担忧是m_piRandom不是 NULL,我还尝试为 a 定义一个默认构造函数,默认情况下 NULL 指针,但这没有帮助。为完全人为的代码道歉..

谢谢

你的第一个错误是你根据赋值运算符实现了复制构造函数。复制构造函数基于其他对象引入新对象,而赋值运算符清除并更改已创建对象的位。

因此,您的正确复制构造函数将是:-

a::a(const a &toCopy) : m_sz(toCopy.m_sz), m_piRandom(new int)
{
    *m_piRandom = toCopy.m_piRandom;
}

实现此操作后,您可以简化赋值运算符:

a& a::operator=(const a &toAssign)
{
    if (this != &toAssign)
    {
        m_sz = toAssign.m_sz;
        if (m_piRandom)           //<<<<< No need to check this as it should always be
        {                         //<<<<< initialized by constructors.
            delete m_piRandom;    
            m_piRandom = NULL;
        }
        m_piRandom = new int(*toAssign.m_piRandom);
    }
    return *this;
}

删除这些冗余后,分配运算符如下所示

a& a::operator=(const a &toAssign)
{
    if (this != &toAssign)
    {
        m_sz = toAssign.m_sz;
        m_piRandom = new int(*toAssign.m_piRandom);
    }
    return *this;
}

发生此错误是因为复制构造函数未初始化m_piRandom 。这意味着变量将(很可能)被垃圾填充(初始化对象时内存位置的任何内容)。

调用顺序如下:

a::a() [doesn not initialize m_piRandom] -> a::operator= -> delete m_piRandom.

要修复:

a::a(const a &toCopy)
: m_piRandom{ nullptr } // <---- add this
{
    operator=(toCopy);
}

编辑:您可以使用复制和交换习惯用法大幅改进赋值运算符。

代码的问题在于复制构造函数不初始化 int 指针 membrer,但赋值运算符为其假定正确的值。因此,只需在调用赋值运算符之前在复制构造函数中将 int 指针初始化为 0。