【问题标题】:[C++]If passing by value creates a local copy, why does the original object get changed? [closed][C++]If passing by value creates a local copy, why does the original object get changed? [closed]
【发布时间】:2017-09-25 08:58:59
【问题描述】:

我在here 上阅读了一个类似的问题,即按值传递将在您的函数中创建对象的本地副本。当我尝试这样做时,我的原始对象被改变了,但我不希望它改变。

此测试的目标是尝试传递一个对象,在本地对其进行更改,但保持原件不变。

在ObjectList头文件中:

int **board;

ObjectLise 类包含一个构造函数和一个打印函数:

ObjectList::ObjectList()
{
    board = new int*[9];
    for(int i = 0; i < 9; i++)
    {
        board[i] = new int[9];
    }

    for(int i = 0; i < 9; i++)
    {
        for(int j = 0; j < 9; j++)
        {
            board[i][j] = 10;
        }
    }
}

void ObjectList::printB()
{
    for(int i = 0; i < 9; i++)
    {
        for(int j = 0; j < 9; j++)
        {
            cout << board[i][j] << ",";
        }
        cout << endl;
    }
}

具有通过 ObjectList 传递的函数的 ChangeBoard 类。

void ChangeBoard::LetsChange(ObjectList layout)
{
    layout.board[0][0] = 99;
    layout.board[1][0] = 99;
    layout.board[2][0] = 99;
    layout.board[3][0] = 99;
    layout.board[4][0] = 99;
    layout.board[5][0] = 99;
}

我主要创建两个对象并将 ObjectList 对象传递给 LetsChange 函数以尝试仅在本地更改对象,即仅在函数中:

ObjectList object = ObjectList();

ChangeBoard change = ChangeBoard();

object.printB();

change.LetsChange(object);
cout << endl;

object.printB();

输出显示原始对象被更改:

10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,

99,10,10,10,10,10,10,10,10,
99,10,10,10,10,10,10,10,10,
99,10,10,10,10,10,10,10,10,
99,10,10,10,10,10,10,10,10,
99,10,10,10,10,10,10,10,10,
99,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,

【问题讨论】:

  • 对象没有被修改,但是board指向的数据会被修改。我建议您阅读有关the rules of three, five and zero 的内容,并停止对数组或向量使用指针。
  • 您的问题缺少minimal reproducible example,但看起来ObjectList 的复制构造函数已损坏。
  • 投票关闭,因为缺乏可重复的例子。
  • 假设您没有明确定义一个,我认为您需要查找复制构造函数行为en.cppreference.com/w/cpp/language/copy_constructor
  • 据我了解,您只能返回指向数组的指针,而不能返回静态数组。这就是我选择使用指针的原因。

标签: c++


【解决方案1】:

如果没有实际复制内存(或合适的标准容器)的复制构造函数,当您按值传递对象时会发生指针成员 board 被复制。 指针 被复制,而不是它指向的数据。这是所谓的副本,而不是副本。

这意味着在ChangeBoard::LetsChange 函数中有两个 对象,其中board 成员都指向同一个位置。 board 成员的快速打印输出或仅使用调试器,您应该很快就能看到。

在函数中你修改了这个board指针指向的内存,而不是对象本身的数据。如果你做了例如layout.board = new int*[9]; 在函数中,您将修改实际成员,并且该更改不会反映在调用函数中。


如果你有一个析构函数delete[] 指向board 指向的内存,那么指针 的复制可以导致undefined behavior。因为那样您将为 both 对象释放内存,并且当您稍后在原始对象中使用 board 成员时,它将不再指向已分配的数据。

这就是你需要考虑the rules of three, five and zero的原因。

我建议您停止使用指针,如果大小是编译时常量,则使用 std::array;如果大小是在运行时设置的,则使用 std::vector

使用std::arraystd::vector,您可以使用the rule of zero,而不必担心处理自己的内存、复制和删除。

【讨论】:

  • 感谢您的澄清。
【解决方案2】:

隐式复制构造函数会在复制对象时复制棋盘指针的内容……所以它仍然会指向原始对象的数据。 explicit copy constructor 可能需要分配和复制board 数组以获得您期望的行为。

【讨论】:

    【解决方案3】:

    正如其他人提到的,你的例子并不完整

    但让我试着澄清一下。
    您的对象 object 是按值传递的,例如LetsChange 函数调用会生成对象的本地副本,例如其成员。

    在您的情况下,board 我认为是您的班级成员ObjectList 将被复制,但不会复制您通过board 指向的分配的内存。

    因此,当您修改内存时,它会在技术上“影响”您拥有的对象。

    【讨论】:

      【解决方案4】:

      您似乎没有提供复制构造函数。默认的复制构造函数只是简单地复制对象的每个成员(当然,如果这个操作是为每个成员定义的)。将创建 board 的副本。但是,指针的副本指向与原始数据相同的数据。因此,您正在通过副本更改在技术上不属于任何对象的原始数据。

      您总是可以通过编写一个复制所指向的数据的复制构造函数来解决这个问题。一个更好的解决方案是去掉 new 和相应的 delete 语句,并用更合适的替代方案替换。

      大多数人会使用std::vector。它将修复您的默认复制构造函数并移动构造函数。它也是一个可变长度数组,您可能会发现它很有用。

      如果您需要强制使用静态大小,std::array 几乎是一个更安全、可复制的 C 样式数组。它不会衰减为指针,它提供了适当的复制构造函数。

      最后,如果您需要一个静态数组但出于某种原因在堆上,std::unique_ptr&lt;&gt;std::shared_ptr&lt;&gt; 可以保存一个数组。他们所做的是在内部管理对象的生命周期。不同之处在于unique_ptr 只允许传递所有权,而shared_ptrs 可以指向同一个对象,并且它们确保在最后一个指针被销毁或重置时进行清理。

      【讨论】:

        猜你喜欢
        • 2022-12-01
        • 2012-09-19
        • 2022-12-26
        • 2022-12-19
        • 2022-12-01
        • 2022-12-27
        • 2022-12-02
        • 2022-12-27
        • 1970-01-01
        相关资源
        最近更新 更多