拷贝构造函数调用时机
内容简介
拷贝构造函数用于“用一个已有对象创建新对象”。理解它的调用时机,是理解值传递、对象返回、深浅拷贝和移动语义的基础。
调用时机
C++ 中拷贝构造函数通常在三类场景中出现:
- 使用一个已经创建完毕的对象初始化新对象。
- 以值传递方式给函数参数传值。
- 以值方式返回对象。
示例类:
class Person {
public:
Person()
{
std::cout << "Person default constructor\n";
}
explicit Person(int age) : age_(age)
{
std::cout << "Person constructor\n";
}
Person(const Person& other) : age_(other.age_)
{
std::cout << "Person copy constructor\n";
}
~Person()
{
std::cout << "Person destructor\n";
}
int age() const { return age_; }
private:
int age_ = 0;
};1. 用已有对象初始化新对象
Person p1(10);
Person p2(p1);
std::cout << p2.age() << '\n';p2(p1) 会调用拷贝构造函数。
2. 值传递参数
void do_work(Person temp)
{
}
Person p(18);
do_work(p);实参传给形参时,相当于创建了一个新的 Person temp = p,因此会调用拷贝构造函数。
如果不希望拷贝,使用引用:
void do_work(const Person& person)
{
}3. 值返回对象
Person create_person()
{
Person p(18);
return p;
}
Person result = create_person();这里理论上可能涉及拷贝或移动,但现代 C++ 编译器通常会做 RVO/NRVO,直接在调用方位置构造对象。
最佳代码实践
- 大对象作为参数时优先传
const T&,避免不必要拷贝。 - 需要保留副本时再按值传递。
- 返回局部对象时直接
return obj;,不要写return std::move(obj);,避免影响 RVO。 - 有资源所有权的类要明确拷贝语义,不能只依赖默认浅拷贝。
错误用法
void print_user(User user)
{
std::cout << user.name() << '\n';
}如果 User 很大或持有资源,这会产生不必要拷贝。更好的写法:
void print_user(const User& user)
{
std::cout << user.name() << '\n';
}注意事项
- C++17 起某些返回值场景强制省略拷贝,但学习阶段仍要理解拷贝构造的概念。
- 拷贝构造和 cpp深拷贝和浅拷贝、移动语义 强相关。
学习路径
- 上一节:cpp构造函数调用规则
- 当前阶段:cppOOP
- 下一节:cpp深拷贝和浅拷贝