# 复制与共享:浅复制与引用隔离
学习目标:通过嵌套配置修改,理解引用别名(Alias)、浅复制(Shallow Copy)与深复制(Deep Copy)的内存行为与隔离边界。
前置知识与范围:结构体/对象与引用。聚焦修改副本时,原对象字段是否产生意料之外的联动变化。
# 问题与边界
定义具有一层嵌套结构的配置:
- 原配置
original:env:"prod"server: 包含host: "127.0.0.1",port: 8080
- 验证三种行为:
- 引用共享(别名):对共享引用的任何修改直接反映在原对象上。
- 浅复制(Shallow Copy):复制顶层对象。修改浅副本的顶层属性
env = "stage",原对象original.env保持"prod";但修改浅副本嵌套的server.port = 9000,原对象original.server.port会被连带篡改! - 深复制(Deep Copy):全层级内存独立。修改深副本的
server.port = 9999,原对象完全不受影响。
# 关键差异
| 语言 | 默认赋值行为 | 浅复制机制 | 深复制机制 |
|---|---|---|---|
| Rust | 默认移动语义(Move);派生 Clone |
结构体直接派生 Clone(值语义直接全深拷贝) |
clone() 默认递归拷贝 |
| Go | 结构体变量赋值为全量值拷贝 | 顶层值复制;若嵌套指针则为浅拷贝 | 递归构造新嵌套结构体 |
| Java | 变量赋值仅传递对象引用 | clone() 或手动复制外层构造 |
递归 new 或序列化深拷贝 |
| JavaScript | 对象赋值为引用传递 | 展开运算符 { ...orig } 或 Object.assign |
原生 structuredClone(orig) |
| TypeScript | 沿用 JS 机制 | 展开运算符 | structuredClone |
| Kotlin | 引用传递 | data class copy() 方法(浅拷贝) |
递归构造独立子对象 |
| Python | 赋值为名称绑定引用 | 标准库 copy.copy(obj) |
标准库 copy.deepcopy(obj) |
| C++ | 默认全量值语义深拷贝 | 嵌套指针时需注意悬挂 | 拷贝构造函数默认深拷贝成员 |
# 各语言实现
# Rust
Rust#[derive(Clone, Debug, PartialEq)]
struct Server {
host: String,
port: u16,
}
#[derive(Clone, Debug, PartialEq)]
struct Config {
env: String,
server: Server,
}
fn main() {
let original = Config {
env: "prod".to_string(),
server: Server { host: "127.0.0.1".to_string(), port: 8080 },
};
// Rust 的派生 Clone 默认对内部所有持有字段进行深度拷贝
let mut cloned = original.clone();
cloned.env = "stage".to_string();
cloned.server.port = 9999;
// 原对象完全不受影响
assert_eq!(original.env, "prod");
assert_eq!(original.server.port, 8080);
assert_eq!(cloned.server.port, 9999);
println!("orig_port: {}, cloned_port: {}", original.server.port, cloned.server.port);
}
Rust 的所有权模型杜绝了隐式别名共享导致的静默数据竞争;Clone 显式复制数据。
# Go
Gopackage main
import "fmt"
type Server struct {
Host string
Port int
}
type Config struct {
Env string
Server *Server // 嵌套指针模拟浅拷贝场景
}
func main() {
original := Config{
Env: "prod",
Server: &Server{Host: "127.0.0.1", Port: 8080},
}
// 浅拷贝:顶层结构体值复制,但内部指针指向同一地址
shallow := original
shallow.Env = "stage"
shallow.Server.Port = 9000
if original.Env != "prod" {
panic("top level should be isolated")
}
if original.Server.Port != 9000 {
panic("nested pointer was shared and modified")
}
// 深拷贝:递归显式分配全新子指针
deep := Config{
Env: original.Env,
Server: &Server{
Host: original.Server.Host,
Port: 8080, // 重置并独立
},
}
deep.Server.Port = 9999
if original.Server.Port != 9000 || deep.Server.Port != 9999 {
panic("deep copy failed")
}
fmt.Printf("orig_port: %d, deep_port: %d\n", original.Server.Port, deep.Server.Port)
}
Go 结构体如果是纯值类型嵌套,赋值直接全量值复制;一旦包含指针切片字段,赋值即退化为浅拷贝。
# Java
Javapublic class Main {
public static class Server {
public String host;
public int port;
public Server(String host, int port) { this.host = host; this.port = port; }
}
public static class Config {
public String env;
public Server server;
public Config(String env, Server server) { this.env = env; this.server = server; }
}
public static void main(String[] args) {
Config original = new Config("prod", new Server("127.0.0.1", 8080));
// 浅拷贝:复制外层,共享内层引用
Config shallow = new Config("stage", original.server);
shallow.server.port = 9000;
assert "prod".equals(original.env);
assert original.server.port == 9000 : "nested instance modified";
// 深拷贝:递归分配独立子对象
Config deep = new Config(original.env, new Server(original.server.host, 9999));
assert original.server.port == 9000;
assert deep.server.port == 9999;
System.out.println("orig_port: " + original.server.port + ", deep_port: " + deep.server.port);
}
}
Java 对象赋值只是复制堆指针;必须递归构造新对象才能切断引用连带修改。
# JavaScript
JavaScriptconst assert = {
equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
deepEqual: (a, b) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
throws: (fn, errType) => {
try { fn(); throw new Error("expected throw"); } catch (e) {
if (e.message === "expected throw") throw e;
if (errType && !(e instanceof errType)) throw e;
}
}
};
const original = {
env: "prod",
server: { host: "127.0.0.1", port: 8080 },
};
// 浅拷贝
const shallow = { ...original };
shallow.env = "stage";
shallow.server.port = 9000;
assert.equal(original.env, "prod");
assert.equal(original.server.port, 9000); // 内层连带被改
// 深拷贝(Node 17+ 原生支持 structuredClone)
const deep = structuredClone(original);
deep.server.port = 9999;
assert.equal(original.server.port, 9000);
assert.equal(deep.server.port, 9999);
console.log(`orig_port: ${original.server.port}, deep_port: ${deep.server.port}`);
标准库 structuredClone 成为现代 JS 进行深度结构拷贝的标准规范。
# TypeScript
TypeScriptconst assert = {
equal: (a: unknown, b: unknown) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
deepEqual: (a: unknown, b: unknown) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
throws: (fn: () => void, errType?: unknown) => {
try { fn(); throw new Error("expected throw"); } catch (e: unknown) {
if (e instanceof Error && e.message === "expected throw") throw e;
if (typeof errType === "function" && !(e instanceof errType)) throw e;
}
}
};
interface Server {
host: string;
port: number;
}
interface Config {
env: string;
server: Server;
}
const original: Config = {
env: "prod",
server: { host: "127.0.0.1", port: 8080 },
};
const shallow: Config = { ...original };
shallow.env = "stage";
shallow.server.port = 9000;
assert.equal(original.env, "prod");
assert.equal(original.server.port, 9000);
const deep: Config = structuredClone(original);
deep.server.port = 9999;
assert.equal(original.server.port, 9000);
assert.equal(deep.server.port, 9999);
console.log(`orig_port: ${original.server.port}, deep_port: ${deep.server.port}`);
TypeScript 在编译时不会阻止对浅拷贝子对象的修改,除非显式使用 Readonly<T> 标记深度只读。
# Kotlin
Kotlindata class Server(var host: String, var port: Int)
data class Config(var env: String, var server: Server)
fun main() {
val original = Config("prod", Server("127.0.0.1", 8080))
// data class 的 copy() 是浅拷贝
val shallow = original.copy(env = "stage")
shallow.server.port = 9000
check(original.env == "prod")
check(original.server.port == 9000)
// 深拷贝
val deep = original.copy(server = original.server.copy(port = 9999))
check(original.server.port == 9000)
check(deep.server.port == 9999)
println("orig_port: ${original.server.port}, deep_port: ${deep.server.port}")
}
Kotlin 的 data class.copy() 仅复制第一层属性,嵌套的可变对象必须逐层级显式 copy()。
# Python
Pythonimport copy
class Server:
def __init__(self, host: str, port: int):
self.host = host
self.port = port
class Config:
def __init__(self, env: str, server: Server):
self.env = env
self.server = server
if __name__ == "__main__":
original = Config("prod", Server("127.0.0.1", 8080))
# 浅拷贝
shallow = copy.copy(original)
shallow.env = "stage"
shallow.server.port = 9000
assert original.env == "prod"
assert original.server.port == 9000
# 深拷贝
deep = copy.deepcopy(original)
deep.server.port = 9999
assert original.server.port == 9000
assert deep.server.port == 9999
print(f"orig_port: {original.server.port}, deep_port: {deep.server.port}")
Python 标准库模块 copy 严格区分并提供 copy.copy(浅拷贝)与 copy.deepcopy(深拷贝)。
# C++
C++#include <iostream>
#include <string>
#include <cassert>
struct Server {
std::string host;
int port;
};
struct Config {
std::string env;
Server server;
};
int main() {
Config original{"prod", {"127.0.0.1", 8080}};
// C++ 默认全值语义赋值就是深拷贝
Config deep = original;
deep.env = "stage";
deep.server.port = 9999;
assert(original.env == "prod");
assert(original.server.port == 8080);
assert(deep.server.port == 9999);
std::cout << "orig_port: " << original.server.port << ", deep_port: " << deep.server.port << "\n";
}
C++ 的值语义体系在缺省情况下对由标准库值类型组成的结构体执行递归全量深复制。
# 迁移误区与适用限制
- 展开运算符不是深拷贝:在 JS/TS 中常误以为
{ ...obj }是完整复制,导致嵌套数组或子对象属性被外部静默污染。 - 循环引用死锁与性能:在深拷贝包含双向指针或循环图结构的对象时,朴素的手写递归会导致堆栈溢出,必须依赖记录访问历史的机制(如
structuredClone或copy.deepcopy的 memo 字典)。
# 运行与验证
语言版本基线见示例基线。 所有程序运行后控制台输出均为:
纯文本orig_port: 9000, deep_port: 9999
浅拷贝连带修改与深拷贝完全隔离断言均已通过(Rust/C++ 针对值语义完全隔离验证)。