拷贝构造函数 (Copy Constructor) 单独拿出来,放在显微镜下看。
这部分逻辑之所以难,是因为它同时也涉及到了内存模型。要彻底理解深拷贝,你脑海里必须有一个动态的画面。
我用**"复印机与仓库"**的类比,配合一行行的代码拆解,带你彻底跑通这个逻辑。
一、 核心逻辑:从"配钥匙"到"建新房"
假设 Lidar 对象就是你家。
-
rpm(转速)是你家墙上的挂钟。 -
data(指针)是你手里的一把钥匙 ,通向一个外部仓库(堆内存),里面放着雷达数据。
1. 浅拷贝 (Shallow Copy) ------ 仅仅是配了一把钥匙
编译器默认干的事,就是简单的"配钥匙"。
-
它把挂钟(
rpm)复制了一个给你朋友。 -
它把钥匙(
data)复制了一把给你朋友。 -
后果 :你和你朋友手里拿着一样的钥匙,打开的是同一个仓库。
-
你朋友打扫仓库(
delete),你的仓库也没了。 -
你再去仓库拿东西,发现空了或者塌了(Crash)。
-
2. 深拷贝 (Deep Copy) ------ 建一个一模一样的仓库
深拷贝构造函数的逻辑是:
-
给我朋友也建一个新的仓库 (
new int[])。 -
把我仓库里的东西,一件一件搬运 到他的新仓库里(
for循环赋值)。 -
最后把新仓库的钥匙给朋友。
二、 代码解剖台:逐行解析
我们把刚才那段代码拆开,每一行都有它的深意。
C++
// 语法解读:
// 1. Lidar: 函数名和类名相同,说明是构造函数。
// 2. const Lidar& other:
// - const: 我保证在拷贝过程中,不会修改原来的那个对象(other)。
// - &: 必须用引用!(后面解释为什么)
Lidar(const Lidar& other) {
// --- 第一步:基本数据的"浅拷贝" ---
// 像 int, bool 这种死板的数据,直接赋值就行。
// 这一步相当于复制了"挂钟"。
this->is_active = other.is_active;
this->rpm = other.rpm;
this->m_size = other.m_size; // 关键:必须知道仓库有多大
// --- 第二步:申请"新地盘" (最关键的一步) ---
// 如果没有这一行,data 就是野指针,或者只是复制了别人的地址。
// new int[m_size] 会在堆内存里开辟一块【全新的、独立的】空间。
// this->data 拿到了这个新空间的地址。
this->data = new int[m_size];
// --- 第三步:搬运数据 ---
// 此时,新仓库是空的(或者里面是垃圾值)。
// 我们需要把 old(other) 仓库里的数据,一个一个填进来。
for(int i = 0; i < m_size; i++) {
// 左边是新仓库,右边是老仓库
this->data[i] = other.data[i];
}
// 此时,两个对象完全独立了。
}
三、 灵魂拷问:为什么要用引用 &?
你可能会问:"我在普通函数传参时,偶尔不用 & 也没事,为什么拷贝构造函数里的参数 (const Lidar& other) 必须加 &?"
这是一个经典的死循环陷阱。
假设你写成这样(去掉了 &):
C++
Lidar(const Lidar other) { ... } // ❌ 错误写法
推演过程:
-
main函数调用Lidar lidar2 = lidar1;。 -
系统准备调用拷贝构造函数。
-
看到参数是
Lidar other(传值)。 -
根据 C++ 规则,传值参数需要被拷贝。
-
为了拷贝
lidar1给other,系统需要再次调用拷贝构造函数。 -
再次调用时,又要传参,又要拷贝......
-
无限套娃 (Infinite Recursion) -> 栈溢出 (Stack Overflow) -> 程序崩溃。
所以,拷贝构造函数的参数必须是引用,这打断了无限拷贝的链条。
四、 什么时候需要深拷贝?
并不是所有类都需要这么麻烦。只有当你的类满足以下**"有产阶级"**特征时才需要:
-
有指针成员 (
int* data,char* buffer)。 -
且 这个指针指向的是动态分配的内存 (
new,malloc)。 -
且 你希望每个对象拥有这份内存的独占权。
车企开发小贴士:
为了避免手写深拷贝这么麻烦且容易出错,现代 C++ (C++11) 给了我们两个神器:
-
std::vector:它自己内部就实现了深拷贝。如果你的data是vector<int>,你根本不需要写拷贝构造函数,编译器默认生成的就够用了(它会调用 vector 的深拷贝)。 -
std::shared_ptr/std::unique_ptr:智能指针明确了所有权,不用你手动new/delete。