代码之恋(第十四篇:分叉的路径与意外的Push)
2025/12/18 21:11:33
下面是 Python、C++ 和 Java 三种语言中数据类型(基本/内置类型)、容器类型(复合/集合类型),以及它们在函数传参、返回值、赋值(=)、push 到 vector/list 等容器时的默认行为(是传值、引用还是指针语义)的详细对比。
&或*)。| 语言 | 数据类型(基本类型) | 容器类型(如 list/vector/map) | 函数传参(默认) | 返回值(默认) | 赋值(a = b) | push 到容器(如 vec.push_back(x)) |
|---|---|---|---|---|---|---|
| Python | int,float,bool,str(不可变)list,dict,set(可变) | 所有容器都是对象(可变) | 传对象引用(但不可变对象表现像传值) | 返回对象引用 | 绑定新名称到对象(不是复制) | 存储对象引用(不复制对象) |
| Java | int,double,boolean等(primitive)String(不可变对象) | ArrayList,HashMap等(对象) | primitive:传值 对象:传引用的值(即“按值传递引用”) | primitive:返回值 对象:返回引用 | primitive:复制值 对象:复制引用(两个变量指向同一对象) | 存储对象引用(不复制对象) |
| C++ | int,double,bool,char等 | std::vector,std::map,std::string等 | 默认传值(复制) (可用 const&避免拷贝) | 默认返回值(移动或拷贝) (RVO/NRVO 优化) | 深拷贝(除非重载=) | 默认 push 值(调用拷贝构造或移动构造) |
int,str,tuple):list,dict):deff(lst):lst.append(4)# 修改原 lista=[1,2,3]f(a)print(a)# [1,2,3,4] → 引用语义int,double等 →传值。String,ArrayList等 →传“引用的副本”(即不能改变原引用,但能修改对象内容)。voidf(ArrayList<Integer>list){list.add(4);// 修改原对象// list = new ArrayList<>(); // 这不会影响调用者}void f(vector<int> v)→ 拷贝整个 vector。void f(const vector<int>& v)→ 推荐,避免拷贝。vector<int> f()→ 通常通过移动语义或RVO 优化避免拷贝。a = b→ 调用拷贝赋值运算符(深拷贝)。vec.push_back(x)→ 调用拷贝构造(若 x 是左值)或移动构造(若 x 是右值)。voidf(vector<int>v){/* v 是副本 */}voidg(constvector<int>&v){/* 不拷贝 */}vector<int>data={1,2,3};vec.push_back(data);// 拷贝vec.push_back(move(data));// 移动(data 变为空)| 操作 | Python | Java | C++ |
|---|---|---|---|
| 传参(基本/不可变) | 引用(表现如值) | 值(primitive) / 引用副本(对象) | 值(拷贝) |
| 传参(容器/可变) | 引用(共享对象) | 引用副本(可修改内容) | 值(拷贝整个容器,除非用&) |
| 返回值 | 返回对象引用 | primitive:值;对象:引用 | 返回值(通常移动或 RVO 优化) |
| 赋值 a = b | a 绑定到 b 所指对象 | primitive:值拷贝;对象:引用拷贝 | 深拷贝(调用 operator=) |
| push 到容器 | 存储对象引用 | 存储对象引用 | 拷贝或移动元素(值语义) |
| 语言 | 内存模型 | 默认语义 | 是否容易意外共享? |
|---|---|---|---|
| Python | 所有都是对象引用 | 引用语义 | ✅ 容易(尤其可变对象) |
| Java | primitive 值 + 对象引用 | 混合(primitive 值,对象引用) | ✅ 对象容易共享 |
| C++ | 值语义为主,可选引用/指针 | 值语义(安全但可能低效) | ❌ 默认不共享(需显式用&或*) |
如有特定场景(如“如何在 C++ 中避免拷贝?”或“Python 如何实现深拷贝?”),可进一步展开。