C++ 拷贝函数的调用时机
在C++中,形参作为实参的副本时是否调用拷贝构造函数,取决于形参的类型和传递方式。以下是详细分析:
1. 按值传递类类型对象:调用拷贝构造函数
当形参是类类型且按值传递时,会调用拷贝构造函数创建形参的副本:
class MyClass {
public:
MyClass() { cout << "默认构造函数" << endl; }
MyClass(const MyClass& other) { cout << "拷贝构造函数" << endl; }
};
void func(MyClass obj) { /* 形参obj是实参的副本 */ }
int main() {
MyClass obj1; // 调用默认构造函数
func(obj1); // 按值传递,调用拷贝构造函数创建形参副本
}
输出:
默认构造函数
拷贝构造函数
2. 按值传递基本类型:不调用拷贝构造函数
对于基本类型(如int、double),按值传递时直接进行值复制,不涉及拷贝构造函数:
void intFunc(int x) { /* 直接复制x的值 */ }
int main() {
int num = 10;
intFunc(num); // 直接复制num的值,不调用任何构造函数
}
3. 按引用传递:不调用拷贝构造函数
如果形参是引用类型(尤其是常量引用),则不会创建副本,而是直接绑定到实参的内存地址:
void refFunc(const MyClass& obj) { /* 直接引用实参,无副本 */ }
int main() {
MyClass obj1;
refFunc(obj1); // 不调用拷贝构造函数,仅引用原对象
}
4. 特殊场景:移动语义与拷贝省略
-
移动构造函数(C++11起):
如果形参是右值引用(如MyClass&&),且类定义了移动构造函数,则按值传递时可能调用移动构造函数而非拷贝构造函数:class MyClass { public: MyClass() {} MyClass(MyClass&& other) { /* 移动构造 */ } }; void moveFunc(MyClass obj) { /* 可能调用移动构造函数 */ } int main() { moveFunc(MyClass{}); // 临时对象右值,优先调用移动构造函数 } -
拷贝省略(RVO/NRVO):
编译器可能优化掉拷贝构造函数调用(如直接在函数返回时构造对象到目标位置):MyClass createObj() { MyClass obj; return obj; // 可能直接构造到调用方的内存,跳过拷贝 }
5. 拷贝构造函数的显式定义与默认行为
- 如果类未显式定义拷贝构造函数,编译器会生成默认拷贝构造函数,对成员进行逐成员浅拷贝。
- 若类包含指针成员且需要深拷贝(如管理动态内存),必须自定义拷贝构造函数:
class DeepCopyClass { int* data; public: DeepCopyClass(const DeepCopyClass& other) { data = new int(*other.data); // 深拷贝 } };
关键结论
| 传递方式 | 是否调用拷贝构造函数 | 是否创建副本 | 是否影响实参 |
|---|---|---|---|
| 按值传递(类类型) | 是 | 是 | 否(副本独立) |
| 按值传递(基本类型) | 否(直接复制值) | 是 | 否 |
| 按引用传递 | 否 | 否 | 是(直接操作实参) |
| 按右值引用传递 | 可能调用移动构造函数 | 是(可移动) | 否 |
总结:形参作为实参副本时,仅当形参是类类型且按值传递才会调用拷贝构造函数。对于基本类型或引用传递,则不会涉及拷贝构造函数。理解这一机制对避免不必要的性能开销(如大对象拷贝)和正确管理资源(如动态内存)至关重要。
浙公网安备 33010602011771号