在C++中分配对象
Assignment of objects in C++
为了将我的问题置于上下文中,我正在使用具有以下定义的矩阵类:
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非常相似,这也是导致我提出这个问题的原因)。问题是,这两种方法是否正确地分配了对象?
这有点复杂。
考虑这个简单的类:
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。这是一个浅层副本。一般来说,如果你想做一个深拷贝(也就是说,每个事物都指向它自己的一个int),你必须手动完成,或者给类一个赋值运算符来处理它。由于您的 Matrix
类没有显式赋值运算符,因此编译器会为其提供默认值 - 这是一个浅拷贝。这就是为什么在您的第一个代码段中,两个矩阵似乎都发生了变化。
编辑:感谢@AlisherKassymov指出表单的声明Thing A=B;
使用复制构造函数,而不是赋值运算符。因此,要使解决方案在上述代码中工作,复制构造函数必须进行深层复制。 (请注意,如果复制构造函数这样做,您几乎肯定希望赋值运算符也这样做(请参阅三法则))。另请注意,如果这些函数变得复杂,则只需让复制构造函数调用赋值运算符即可。
这两者并不相等。
第一个代码段
Matrix test
,是完整的内容被复制到Matrix ptr
。稍后使用 ptr
时,仅更改副本,而不更改原始Matrix test
。
第二个片段
Matrix test
的地址被放入指针Matrix *ptr
。指针现在保存 test
的地址。当你写(*ptr)
时,你取消引用指针并使用原始test
的值。
在爪哇语中
在Java中,所有对象都是指针(像int
这样的原语不是)。当您将一个对象分配给另一个对象时,默认设置是仅覆盖指针值。工作原理类似于您的第二个示例。
第一个将test
的值复制到ptr
中。第二个将 ptr 设置为指向 test
地址的指针
这两个操作是不一样的。在第一种情况下,ptr
将具有与 test
相同的值,但它们在其中是该数据的两个不同副本,因此您对ptr(0,0) = 95;
的分配不会设置test(0, 0)
。
然而,在第二个实例中,ptr
指向test
的地址,因此ptr
的取消引用是测试。因此,当您在此处设置值时,您实际上也在设置test(0, 0)
的值。
这可以通过这样的程序轻松验证:
#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";
}
当然,如果您动态分配矩阵(新),那么当您按照第一个示例复制值时,指向数据的指针也会被复制,因此当您设置新数据时,它似乎等于第二个示例。
在这种情况下,Java 行为的等效项使用C++引用表示
Matrix test(4,4);
Matrix &ptr = test;
ptr(0,0) = 95;
此代码确实与指针版本执行相同的操作,即它修改原始test
对象。
您的第一个代码示例正式创建原始对象的副本,然后修改该副本。但是,您的Matrix
类似乎是一个拥有一些较低级别内存(matrix
指针)的多级对象。如果您的 Matrix
类复制构造函数实现了浅拷贝逻辑(即它与原始对象共享较低级别的矩阵数据,而不是深拷贝),则修改副本也会显示为修改原始对象。此行为是否正确取决于您的意图。
在您的评论中,您提到第一个代码似乎也修改了原始对象。这立即意味着您的类实际上实现了浅层复制逻辑。看起来它不是您设计的预期部分。显然,您在实现Matrix
类时忘记遵循三法则。
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- C++ 如何在将新对象分配给另一个对象时创建新对象
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- 为对象分配整数.输出将是什么?
- C++ 使用枚举类对象分配 std::map 值
- 如何在C++中为增加但记住删除先前对象的对象分配唯一标识符
- 我重载了 << 和 = 运算符。为什么当我将一个对象分配给另一个对象并尝试打印它时,我会被打印出来?
- 为什么将两个对象分配给另一个对象后,两个对象不一样?
- 有没有办法在同名类 (c++) 中为对象分配一个指针变量
- 如何使用函数(而不是构造函数)将派生类对象分配给基类指针
- 是否可以使用 malloc 为类对象分配内存?
- 将对象分配给数组C++
- 我们可以在cpp中为对象分配函数吗
- 为模板参数类型中的新对象分配内存
- 我无法将对象分配给对象数组
- 对象分配-成员函数的使用无效错误
- 将派生对象分配给函数内的基类指针
- Arduino ESP32如何将BLEUUID对象分配到字符串中
- Rapidjson将密钥值从一个文档对象分配到另一个文档对象
- 奇怪的对象分配行为C