C++拷贝构造初始化:为何const Animal c = b等价于Animal c(b)
关于拷贝构造两种初始化写法的等价性说明
核心结论
在你给出的示例场景中,const Animal c = b;和const Animal c(b);完全等价,执行逻辑没有任何差异,二者都会直接调用你实现的Animal(const Animal &a)拷贝构造函数完成对象c的初始化。
初始化语义的基础规则
C++中对象的初始化和赋值是完全不同的两个行为,不要被=符号误导:
注意:只要
=出现在新对象定义的语句中,它就是初始化语义,不是赋值语义。赋值操作仅作用于已经构造完成的已有对象,比如你示例中的a = c;,a在这行之前已经通过默认构造函数创建完成,所以这行才会调用operator=拷贝赋值运算符。
你用到的两种写法分别对应两类初始化语法:
- 直接初始化:语法为
类型 变量名(构造参数),即const Animal c(b),编译器会直接根据传入的参数类型匹配对应的构造函数调用。 - 拷贝初始化:语法为
类型 变量名 = 初始化值,即const Animal c = b。
二者等价的原因
当拷贝初始化的等号右侧是和左侧完全相同的类型时,C++标准明确规定这种场景会直接匹配拷贝构造函数调用,不会生成临时对象,也不会走赋值逻辑,和直接初始化的执行路径完全一致。
你可以直接运行示例代码验证:无论你切换哪种写法,控制台都只会打印一次copy constructor,不会出现assignment operator的输出,和上述规则完全吻合。
二者存在差异的特殊场景
只有当拷贝初始化等号右侧的类型和左侧待初始化对象的类型不匹配时,两种写法才会出现行为差异:
如果类存在带单参数的非explicit构造函数,比如Animal(const std::string& name):
- 直接初始化
Animal d("dog")会直接匹配该构造函数,始终合法。 - 拷贝初始化
Animal d = "dog"会先尝试做隐式类型转换,将字符串字面量转为std::string再构造Animal对象;如果该构造函数被explicit关键字修饰,禁止隐式转换,这行代码会直接编译失败。
你当前的场景中等号两侧都是Animal类型,不存在类型转换步骤,因此两种写法没有任何区别。
完整可运行示例代码
#include <cstdio> #include <string> const std::string unk = "unknown"; const std::string clone_prefix = "clone-"; // -- interface -- class Animal { std::string a_type = ""; std::string a_name = ""; std::string a_sound = ""; public: Animal(); // default constructor Animal(const std::string & type, const std::string & name, const std::string & sound); Animal(const Animal &); // copy constructor ~Animal(); // destructor Animal & operator = (const Animal &); // copy operator void print() const; }; // -- implementation -- Animal::Animal() : a_type(unk), a_name(unk), a_sound(unk) { puts("default constructor"); } Animal::Animal(const std::string & type, const std::string & name, const std::string & sound) : a_type(type), a_name(name), a_sound(sound) { puts("constructor with arguments"); } Animal::Animal(const Animal & a) { puts("copy constructor"); a_name = clone_prefix + a.a_name; a_type = a.a_type; a_sound = a.a_sound; } Animal::~Animal() { printf("destructor: %s the %s\n", a_name.c_str(), a_type.c_str()); a_name = ""; a_type = ""; a_sound = ""; } Animal & Animal::operator = (const Animal & o) { puts("assignment operator"); if(this != &o) { a_name = clone_prefix + o.a_name; a_type = o.a_type; a_sound = o.a_sound; } return *this; } void Animal::print () const { printf("%s the %s says %s\n", a_name.c_str(), a_type.c_str(), a_sound.c_str()); } int main() { Animal a; a.print(); const Animal b("goat", "bob", "baah"); b.print(); const Animal c = b; // 切换成下面的直接初始化写法,输出完全一致 // const Animal c (b); c.print(); a = c; a.print(); puts("end of main"); return 0; }
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

