拷贝构造函数调用时机

内容简介

拷贝构造函数用于“用一个已有对象创建新对象”。理解它的调用时机,是理解值传递、对象返回、深浅拷贝和移动语义的基础。

调用时机

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';
}

注意事项

学习路径