You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何将line构造函数参数改为const引用后,point拷贝构造仍被调用2次?

为什么将line构造函数参数改为const引用后仍会调用两次拷贝构造?

相关C++代码

class point
{
public:
    point(double x, double y) : x(x), y(y)
    {
        std::cout << "point parameterized constructor of: "
        << getThis() << std::endl;
    }

    point(const point& that) : x(that.x), y(that.y)
    {
        std::cout << "point copy constructor of: "
        << getThis() << std::endl;
    }

    ~point()
    {
        std::cout << "point destructor of: "
        << getThis() << std::endl;
    }
private:
    double x;
    double y;
    point* getThis() { return this; }
};

class line
{
public:
    line(const point& startPoint, const point& endPoint) :
    startPoint(startPoint),
    endPoint(endPoint)
    {
        std::cout << "line parameterized constructor: " << getThis() << std::endl;
    }
    ~line()
    {
        std::cout << "line destructor of: "
        << getThis() << std::endl;
    }
private:
    point startPoint;
    point endPoint;
    line* getThis() { return this; }
};

int main()
{
    point p1(3.0, 4.0);
    point p2(5.0, 6.0);

    line l1(p1, p2);

    return 0;
}

程序输出

point parameterized constructor of: 0x577fffc00
point parameterized constructor of: 0x577fffbe0
point copy constructor of: 0x577fffbb0
point copy constructor of: 0x577fffbc8
line parameterized constructor of: 0x577fffbb0
line destructor of: 0x577fffbb0
point destructor of: 0x577fffbc8
point destructor of: 0x577fffbb0
point destructor of: 0x577fffbe0
point destructor of: 0x577fffc00

背景情况

最初line的构造函数参数是值传递:

line(point startPoint, point endPoint) : startPoint(startPoint), endPoint(endPoint) {...}

此时Clang-Tidy给出警告:

Clang-Tidy: The parameter 'endPoint' is copied for each invocation but only used as a const reference; consider making it a const reference

值传递时程序输出里point的拷贝构造函数被调用4次,改成const引用参数后,拷贝构造调用次数减半为2次,但原本以为会完全消除拷贝构造调用,特此询问原因。


原因解析

核心问题出在line类的私有成员上:point startPoint和point endPoint是独立的成员对象,不是引用或指针。

当构造函数参数是const引用时,初始化列表里的startPoint(startPoint)本质是把引用指向的外部对象(main里的p1)拷贝到line自身的startPoint成员中,这必然会触发一次point的拷贝构造;同理endPoint(endPoint)是拷贝p2到line的endPoint成员,又触发一次拷贝构造——这就是输出里两次拷贝构造的来源。

而之前用值传递时,会发生两次额外的拷贝:调用line构造函数时,p1会先拷贝到函数参数startPoint,p2拷贝到参数endPoint;接着初始化成员时,又从这两个参数各拷贝一次到成员对象。总共2(参数拷贝)+2(成员初始化拷贝)=4次拷贝,这也是改成引用后次数减半的原因。

如何完全消除拷贝?

如果想彻底避免拷贝构造调用,有两种常见方案:

  1. 将line的成员改为引用类型:
    class line
    {
    public:
        line(const point& startPoint, const point& endPoint) :
        startPoint(startPoint), endPoint(endPoint) {}
    private:
        const point& startPoint;
        const point& endPoint;
    };
    
    注意:必须保证被引用的对象(比如main里的p1、p2)生命周期长于line对象,否则会出现悬空引用问题。
  2. 使用移动语义:如果point实现了移动构造函数,当传入临时对象时可以用移动代替拷贝;如果传入命名对象,可通过std::move转移所有权,但这会让原对象进入有效但未定义的状态,需根据业务场景判断是否适用。

内容的提问来源于stack exchange,提问作者nyms1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 15:01:20