我在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;
}
}
ChangeBoard类,包含传递给ObjectList的函数。
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;
}
在main中我创建了两个对象并将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,
答案 0 :(得分:2)
如果没有实际复制内存(或合适的标准容器)的复制构造函数,那么当您按值传递对象时会发生指针成员board
的复制。复制 指针 ,而不是它指向的数据。它是一个所谓的浅副本,而不是深副本。
这意味着在ChangeBoard::LetsChange
函数中您有两个对象,其中board
成员都指向同一位置。快速打印board
成员或只使用调试器,您应该很快就能看到这一点。
在函数中,您修改此board
指针指向的内存,而不是对象本身的数据。如果你做了,例如在函数中layout.board = new int*[9];
,您将修改实际成员,并且该更改不会反映在调用函数中。
如果你有delete[]
由board
指向的内存的析构函数,那么指针的复制可能会导致undefined behavior。因为那时你将释放两个对象的内存,当你以后在原始对象中使用board
成员时,它将不再指向已分配的数据。
这是您需要考虑the rules of three, five and zero。
的原因我建议您停止使用指针,如果大小是编译时常量,则使用std::array
;如果在运行时设置大小,则使用std::vector
。
使用std::array
或std::vector
,您可以使用the rule of zero,而无需担心处理自己的内存,复制和删除。
答案 1 :(得分:1)
隐式复制构造函数将在复制对象时复制板指针的内容...因此它仍将指向原始对象的数据。 ex plicit copy constructor可能需要分配和复制board
数组以获得您期望的行为。
答案 2 :(得分:0)
正如其他人所说,你的例子并不完整
但是让我试着清楚一点
您的对象object
按值传递,例如LetsChange
函数调用会生成对象的本地副本,例如其成员。
在您的情况下,我认为board
是您班级ObjectList
的成员将被复制,但不会被分配到board
指向的内存。
因此,当您修改内存时,它将在技术上“影响”您拥有的对象。
答案 3 :(得分:0)
您似乎没有提供复制构造函数。默认的复制构造函数只是复制对象的每个成员(如果为每个成员定义了此操作)。创建了board
的副本。但是,指针的副本指向与原始数据相同的数据。因此,您正在通过副本更改技术上不属于任何对象的原始数据。
您始终可以通过编写复制指向的数据的复制构造函数来解决此问题。更好的解决方案是删除new
和相应的delete
语句,并替换为更合适的替代方法。
大多数人会使用std::vector
。它将修复您的默认复制构造函数并移动构造函数。它也是一个可变长度数组,您可能会发现它很有用。
如果您需要强制执行静态大小,std::array
几乎是一个更安全,可复制的C风格数组。它不会衰减到指针,它提供了正确的复制构造函数。
最后,如果由于某种原因需要静态数组但在堆上,std::unique_ptr<>
和std::shared_ptr<>
可以保存数组。他们所做的是在内部管理对象的生命周期。区别在于unique_ptr
仅允许传递所有权,而shared_ptr
s可以指向同一个对象,并确保在最后一个指针被销毁或重置时进行清理。