要对我的问题进行语境化,我正在使用具有以下定义的Matrix类:
Matrix(unsigned int, unsigned int); // matrix of the given dimension full of zeroes
Matrix(Matrix*); // creates a new matrix from another one
int &operator()(int, int); // used to access the matrix
int **matrix; // the matrix
现在接受以下两个代码段:
首先:
Matrix test(4,4);
Matrix ptr = test;
ptr(0,0) = 95;
第二
Matrix test(4,4);
Matrix *ptr = &test;
(*ptr)(0,0) = 95;
两个代码都有相同的效果,(0,0)位置的元素接收95(第一个代码片段与Java非常相似,这是导致我提出这个问题的原因)。 问题是,两种方式都正确地分配了对象吗?
答案 0 :(得分:24)
这有点复杂。
考虑这个简单的类:
class Thing1
{
public:
int n;
}
现在我们尝试第一个实验:
Thing1 A;
A.n = 5;
Thing1 B = A;
B.n = 7;
cout << A.n << " " << B.n << endl;
结果是“5 7”。 A
和B
是两个独立的独立对象。改变一个不会改变另一个。
第二次实验:
Thing1 *p = &A;
p->n = 9;
cout << A.n << " " << p->n << endl;
结果是“9 9”; p
是指向A
的指针,因此A.n
和p->n
是相同的。
现在事情变得复杂了:
class Thing2
{
public:
int *p;
};
...
Thing2 A;
A.p = new int(2);
Thing2 B = A;
*(B.p) = 4;
cout << *(A.p) << " " << *(B.p) << endl;
现在结果是“4 4”。赋值B = A
复制了指针,因此虽然A
和B
是两个不同的对象,但它们的指针指向相同的int。这是一个浅拷贝。一般来说,如果你想制作一个深层拷贝(也就是说,每个Thing指向它自己的一个int)你必须手工完成它或者给一个赋值运算符< / em>将处理它。由于您的Matrix
类没有显式赋值运算符,编译器会为其赋予默认值 - 这是一个浅表副本。这就是为什么在你的第一个片段中,两个矩阵似乎都被改变了。
编辑:感谢@AlisherKassymov,指出表单Thing A=B;
的声明使用复制构造函数,而不是赋值运算符。 因此,要使解决方案适用于上述代码,复制构造函数必须进行深层复制。 (请注意,如果复制构造函数执行它,您几乎肯定希望赋值运算符也这样做(请参阅Rule of Three)。另请注意,如果这些函数变得复杂,只需让复制构造函数调用就可以了赋值运算符。)
答案 1 :(得分:5)
这两者并不相等。
第一个代码段
Matrix test
,已将完整内容复制到Matrix ptr
。
当您稍后使用ptr
时,您只会更改副本,而不是原始Matrix test
。
第二个代码段
Matrix test
的地址放入指针Matrix *ptr
。
指针现在保存test
的地址。
当您编写(*ptr)
时,您取消引用指针并使用原始test
的值。
在Java中
在java中,所有对象都是指针(像int
这样的基元不是)。当您将一个对象分配给另一个对象时,默认情况下仅覆盖指针值。就像你的第二个例子一样。
答案 2 :(得分:2)
第一个将test
的值复制到ptr
。
第二个将ptr设置为指向test
这两个动作并不相同。在第一种情况下,ptr
将与test
具有相同的值,但它们在其中包含两个不同的数据副本,因此ptr(0,0) = 95;
的分配不会设置{{ 1}}。
然而,在第二个实例中,test(0, 0)
指向ptr
的地址,因此test
的取消引用是测试。因此,当您在此处设置值时,您实际上也设置了ptr
的值。
这可以通过以下程序轻松验证:
test(0, 0)
当然,如果您动态分配Matrix(新),那么当您按照第一个示例复制值时,也会复制指向数据的指针,因此当您设置新数据时,它似乎等于第二个例子。
答案 3 :(得分:1)
在这种情况下,Java行为的等效表达式使用C ++ references
表示Matrix test(4,4);
Matrix &ptr = test;
ptr(0,0) = 95;
此代码确实与指针版本完全相同,即它修改了原始test
对象。
您的第一个代码示例正式创建原始对象的副本,然后修改该副本。但是,您的Matrix
类似乎是一个多级对象,拥有一些较低级别的内存(matrix
指针)。如果你的Matrix
类复制构造函数实现了浅复制逻辑(即它与原始对象共享低级矩阵数据而不是深层复制它),那么修改副本也会出现修改原始对象的问题。 。此行为是正确还是不正确取决于您的意图。
在您的评论中,您提到第一个代码似乎也会修改原始对象。这立即意味着您的类实际上实现了浅复制逻辑。看起来它不是您设计的预期部分。显然,当您实施Matrix
课程时,您忘记了Rule of Three。