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

C++创建A类对象时为何调用xINT的拷贝构造函数?

问题分析:A类对象创建时xINT拷贝构造与赋值运算符被调用的原因

首先看你提供的代码:

class xINT
{

private:
    int num;
    static int countCtor;
    static int countDtor;
    static int countCopycon;
    static int countOpAssign;

public:
    xINT()
    {
        std::cout << this << ": " << "ctor" << std::endl;
        ++countCtor;
    }

    ~xINT()
    {
        std::cout << this << ": " << "dtor" << std::endl;
        ++countDtor;
    }

    xINT(const xINT& x)
    {
        std::cout << this << ": " << "copycon of " << std::dec << &x << std::endl;
        num = x.getNum();
        ++countCopycon;
    }

    xINT& operator=(const xINT& x)
    {
        if (&x == this)
        {
            std::cout << "op=" << std::endl;
            //return *this; 
        }

        std::cout << this << ": " << "op= of " << std::dec << &x << std::endl;
        num = x.getNum();
        ++countOpAssign;
        return *this;
    }

    int getNum() const { return num; }
    void setNum(int val) { num = val; }
    static void statistik(std::ostream&);
    static void reset();
};

class A
{
public:
    A(xINT i)
    { i_ = i;
    } 
private:
    xINT i_;
};

int main()
{
    
    xINT i;
    cout << &i << endl;
    i.setNum(42);
    {
        std::cout << "Class A:" << std::endl;
        A a(i);
    }
}

调试时发现执行A a(i);创建A类对象时,程序先进入xINT的拷贝构造函数,之后还会调用xINT的赋值运算符重载,以下是具体原因和特殊成员函数的调用场景:

现象原因

问题出在A类的构造函数实现上:

A(xINT i)
{ 
    i_ = i;
} 

执行A a(i);时,会依次发生三个关键操作:

  1. 形参传递触发拷贝构造:A的构造函数参数是值传递(xINT i),所以main中的i会被拷贝一份作为构造函数的形参,这一步调用xINT的拷贝构造函数。
  2. 成员变量默认构造:进入构造函数体之前,A的成员i_会被自动默认构造(因为构造函数没有用初始化列表显式初始化它),这一步会调用xINT的无参构造函数(你可以从输出的ctor日志看到这一步)。
  3. 赋值运算符被触发:构造函数体里的i_ = i;是给已经存在的i_对象赋值,所以会调用xINT的赋值运算符重载。

这就是你看到两次特殊成员函数调用的根本原因。

优化建议(可选)

如果想避免多余的默认构造+赋值操作,改用初始化列表直接初始化成员变量,这样能把步骤2和3合并成一次拷贝构造:

// 用初始化列表直接拷贝构造i_
A(xINT i) : i_(i) {}

进一步可以把参数改成const引用,避免形参的拷贝操作,提升效率:

// 形参用const引用减少一次拷贝
A(const xINT& i) : i_(i) {}

这样整个过程只会触发一次拷贝构造,没有多余的赋值操作。

特殊成员函数的调用场景

拷贝构造函数

  • 用已存在的对象初始化新对象:比如xINT a = b;、xINT a(b);
  • 函数参数按值传递:比如定义void func(xINT val),调用func(b)时会触发拷贝构造
  • 函数按值返回对象:某些情况下编译器会优化掉,但标准允许这种场景下调用拷贝构造

赋值运算符重载

  • 给已存在的对象赋值另一个对象:比如a = b;,这里a必须是已经完成构造的对象
  • 核心区别:拷贝构造是创建新对象,赋值是修改已有对象的状态,这是两者最关键的不同点

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:58:37