[C++]“=”详解
引言
C++的“=”实际有很多很多陷阱。
在大部分情况下,“=”表示赋值,尤其是C语言学过来的,更有这样的体会。以下两者的执行结果是等效的:
// 示例A int a = 3; // 示例B int a; a = 3;
难免会有直观的认识,int a = 3 中,首先创建一个int的名为a的对象,然后将3赋值给a。而示例B中,将这一过程拆分成了两部。
↑↑恩,万恶的根源↑↑
C++中“=”的语义分类
“=”其实有两种作用:1. 参与构造;2. 赋值。
参与构造类似于初始化,是狭义的初始化(或者是说协助定义,异或有其他更好的说法?)——在一些场合,对从未赋值过的对象进行第一次赋值,也叫做初始化——而这里的“初始化”仅指构造完成对象的这一过程。
// 传统意义上的初始化 int a; // 定义了一个对象←其实是内置类型,C++好绕啊=_= a = 3; // 对其初始化←实际上就是赋值了而已 // 本文所说的初始化和赋值 int a; // 定义了一个对象←其实已经构造完毕了,因为没用=,所以默认初始化的 // 有的情况下,默认初始化为了0xCDCDCDCD // 有的情况下,默认初始化为了内存存放的旧值 a = 3; // 对于赋值
以下的例子说明了int a = 3; 和 int a; a = 3; 是不一样的:
static int func1_i; // static duration变量默认为0 int func1(){ static int i = func1_i; // 这里的=参与的是初始化 // 因为static的函数成员只在第一次使用时构造, //所以只执行了1次 return i++; } static int func2_i; int func2(){ static int i; // 同上,这一句只执行1次 i = func2_i; // 这是赋值,每次都要执行 return i++; } int main(int argc, char* argv) { using namespace std; cout << func1(); cout << func1() << endl; // 输出 01 cout << func2(); cout << func2() << endl; // 输出 0 0,因为每次都重新赋值为0 getchar(); }
这一问题,在C中并不显著,但是在C++却很重要,因为——C++有对象(C你就继续单身着吧)
除了赋值,还有构造
对于内置类型,是无法探寻其实现代码的,C++保证语义上符合这一归于。但是对于用户定义的类就不一样了,我们可以自己书写代码,来验证这一语义。
struct A{ // 默认构造函数 A() {std::printf("default ctor\n");} // 隐式转换构造函数 A(int) {std::printf("ctor with para int\n");} // 赋值 A& operator=(int) {std::printf("operator=\n"); return *this;} }; int main(int argc, char* argv) { using namespace std; A a1; // 输出default ctor,调用了默认构造 a1 = 3; // 输出operator=,调用了赋值 A a2 = 3; // 输出ctor with para int,调用了带参数的构造 getchar(); }
切记,ClassA obj = value; 绝不是创建一个对象obj,然后把value赋值给obj那么简单。
注意类型转换
上边例子的赋值3,可以改为浮点数,编译器会尝试将浮点数转为整数,然后调用响应的函数。
但是,如果存在转换不了的情况,例如string到int,编译器则会报错。
struct A{ A() {std::printf("default ctor\n");} A(int) {std::printf("ctor with para int\n");} A& operator=(int) {std::printf("operator= with para int\n"); return *this;}
// 书写了一个无法转换为int的参数的类型 A& operator=(std::string) {std::printf("operator= with para string\n"); return *this;} }; int main(int argc, char* argv) { using namespace std; string str = "str"; A a1; // 默认构造 a1 = str; // 输出operator= with para string赋值 A a3 = str; // 报错,提示没有这种类型的ctor不存在,需要一个A(string)的构造函数 getchar(); }