在C++中使用浅拷贝而不是引用的原因

Reasons to use a shallow copy instead of a reference in C++?

本文关键字:引用 C++ 浅拷贝      更新时间:2023-10-16

我很好奇,在什么情况下,你会真正创建一个对象的浅拷贝,而不是简单地在C++中传递对该对象的引用或对所述对象进行深度拷贝?

我的理解是,如果你有一个只读或不可变的对象,你只需要使用一个引用。如果您需要在不更改原始数据的情况下对副本进行更改,或者将一些数据封送到不同的内存地址空间,则需要进行深度复制。

我可以看到在写时复制的情况下(在你写对象并完成深度复制之前)使用浅层复制,但你想使用浅层副本还有其他原因吗?

编辑:试图澄清问题。

这里表上的两个选项(假设C++11)是:

  1. 一个具有浅复制构造函数/赋值运算符的对象。

  2. 具有的对象要么复制成本高,要么根本不可复制。给定C++11,它将有一个浅移动构造函数/赋值运算符。

C++最终是一种面向价值的语言。直到C++17,像这样简单的东西:

T t = someFuncReturningT();

至少在理论上会引发复制/移动操作。C++17的保证省略确保了这种情况不会发生,但在那之前,这被认为是执行到T中的复制/移动。当然,几乎每个编译器都会忽略它。但理论上它仍然存在。

然而,对于移动操作,没有浅拷贝并不是一件坏事。

您想要浅层复制与昂贵的复制+浅层移动的主要原因是复制不仅仅是"浅层复制"的类型。例如CCD_ 2。复制不仅仅是复制一两个指针;您还可以增加引用计数。事实上,这是shared_ptr作为一种类型设计的结果:允许一个对象有多个所有者。

唯一的方法是每个所有者都有自己的副本。因此,在这方面,复制必须是"肤浅的"。

因此,优势在于它是对象设计的一部分。


如果你谈论的是C++之前的11天,浅拷贝更有意义。在C++11之后,你可以依靠廉价的移动操作,但在C++11之前,这是不可用的。因此,浅拷贝几乎是合理地按值返回对象的唯一方法。

想象一下下面的例子:

shared_ptr<T> p1(new T);
...
shared_ptr<T> p2 = p1;

这里p2是p1的浅拷贝。你本可以在这里使用参考,但如果你归还它,你就不能:

shared_ptr<T> f() {
shared_ptr<T> p1(new T);
return p1;
}

我首先想到的是移动一个对象。

假设你在课堂上用新操作符分配了一个内存。

struct A 
{
A ()
{
data = new int[50];
}
void destroy()
{
delete [] data;
} 
A ( const A& copy )
{
data = copy.data;
}

int* data 
}

比方说你想复制这个对象。为什么不只是复制指向数据的指针,而是复制所有数据呢。

A calculateA()
{
A returnVal;
for ( int i = 0; i < 50; i++ )
returnVal.data[i] = i;
return returnVal;
}

在这种情况下,浅拷贝可能是有利的。

A ( const A& copy )
{
data = copy.data;
}
A newA =  calculateA(); 
// do stuff with newA
newA.destroy();

我相信还有成千上万的其他原因。

为了避免复制时重复删除,可以将副本设置为null。

#include <iostream>
#include <utility>
struct A 
{
A ()
{
data = new int[50];
}
~A ()
{
if ( data )
delete [] data;
}
A ( A& copy )
{
data = copy.data;
copy.data = nullptr;
}
int* data;
};

int main()
{
A aVal;
for ( int i = 0; i < 50; i++ )
aVal.data[i] = i;
A anotherVal = aVal;
int i = 10;
while (i--)
std::cout << anotherVal.data[i]; 
}