16

为了使我的问题上下文化,我使用了具有以下定义的 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 非常相似,导致我问这个问题的原因)。问题是,这两种方法都正确地分配了对象吗?

4

4 回答 4

33

这有点复杂。

考虑这个简单的类:

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.np->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复制了指针,因此虽然AB是两个不同的对象,但它们的指针指向同一个 int。这是一个浅拷贝。一般来说,如果你想制作一个深拷贝(也就是说,每个 Thing 都指向它自己的一个 int),你必须要么手工完成,要么给类一个赋值运算符来处理它。由于您的Matrix类没有显式赋值运算符,因此编译器为其提供了默认值——即浅拷贝。这就是为什么在您的第一个片段中,两个矩阵似乎都已更改。

编辑:感谢@AlisherKassymov,指出表单Thing A=B;的声明使用复制构造函数,而不是赋值运算符。因此,要使上述代码中的解决方案起作用,复制构造函数必须进行深层复制。(请注意,如果复制构造函数这样做,您几乎肯定希望赋值运算符也这样做(参见三法则))。另请注意,如果这些函数变得复杂,则简单地让复制构造函数调用赋值运算符是有意义的。)

于 2013-11-12T02:19:53.017 回答
5

These two are not equal.

The first snippet

Matrix test, with is full content is COPIED into Matrix ptr. When you work with ptr later, you only change the copy, not the original Matrix test.

The second snippet

The address of Matrix test is put into the pointer Matrix *ptr. The pointer now holds the address of test. When you write (*ptr), you dereference the pointer and work with the value of the original test.

In Java

In java, all objects are kindof pointers (primitives like int are not). When you assign one object to another there, the default is to only overwrite the pointer value. Works like your second example.

于 2013-11-12T01:55:26.040 回答
3

The first copies that value of test into ptr. The second sets ptr to be a pointer to the address of test

These two actions are not the same. In the first case, ptr will have the same value as test, but they are in them selves two distinct copies of that data, so that your assignment of ptr(0,0) = 95; will not set test(0, 0).

In the second instance however, ptr points to the address of test, so that the dereference of ptr is test. Thus, when you set the value here, you are actually setting the value of test(0, 0) as well.

This is trivially verifyable with a program such as this:

#include <iostream>
class Test{
public:
    int i;
};

int main(){
    Test c;
    c.i = 1;

    Test d = c;
    d.i = 2;
    std::cout << "Value: " << c.i << "\n";

    Test* e = &c;
    (*e).i = 2;
    std::cout << "Pointer: " << c.i << "\n";
}

Of course, if you dynamically allocate your Matrix (new) then when you copy the value as per your first example, the pointer to the data is also copied, so when you set the new data, it appears to be equal to the second example.

于 2013-11-12T01:56:17.673 回答
1

在这种情况下,Java 行为的等价物使用 C++引用表示

Matrix test(4,4);
Matrix &ptr = test;
ptr(0,0) = 95;

这段代码确实与指针版本做同样的事情,即它修改了原始test对象。

您的第一个代码示例正式创建原始对象的副本,然后修改该副本。但是,您的Matrix类似乎是一个拥有一些较低级别内存(matrix指针)的多级对象。如果您的Matrix类复制构造函数实现了浅复制逻辑(即,它与原始对象共享较低级别的矩阵数据,而不是对其进行深度复制),那么修改副本似乎也修改了原始对象。这种行为是正确还是不正确取决于您的意图。

在您的评论中,您提到第一个代码似乎也修改了原始对象。这立即意味着您的类实际上实现了浅拷贝逻辑。看起来它不是您设计的预期部分。显然,当您实施课程时,您忘记了遵循三法则。Matrix

于 2013-11-12T02:14:06.917 回答