本文的目的是为摸清类类型转换、对象初始化期间到底发生了什么
设计了3段代码:
①
class CLASS_TYPE{
public:
int a;
CLASS_TYPE(int dd) : a(dd)
{
print("fun A ");
}
CLASS_TYPE(const CLASS_TYPE & bb)
{
a = bb.a;
print("fun B ");
}
CLASS_TYPE & operator=(const CLASS_TYPE & rhs)
{
a = rhs.a;
print("fun C");
}
};
②
class CLASS_TYPE{
public:
int a;
CLASS_TYPE(int dd) : a(dd)
{
print("fun A ");
}
explicit CLASS_TYPE(const CLASS_TYPE & bb)
{
a = bb.a;
print("fun B ");
}
CLASS_TYPE & operator=(const CLASS_TYPE & rhs)
{
a = rhs.a;
print("fun C ");
}
};
③
class CLASS_TYPE{
public:
int a;
explicit CLASS_TYPE(int dd) : a(dd)
{
print("fun A ");
}
CLASS_TYPE(const CLASS_TYPE & bb)
{
a = bb.a;
print("fun B ");
}
CLASS_TYPE & operator=(const CLASS_TYPE & rhs)
{
a = rhs.a;
print("fun C ");
}
};
然后对这3中类定义执行两种复制初始化(一种是从已定义的对象复制初始化,一种是从整形复制初始化),再执行两种复制操作。
结果如下表:
na | 语句 | ① | ② | ③ |
场景1 | CLASS_TYPE objb = obja; | fun B | 编译错误 | fun B |
场景2 | CLASS_TYPE objb = 33; | fun A | 编译错误 | 编译错误 |
场景3 | objb = obja; | fun C | fun C | fun C |
场景4 | objb = 33; | fun A fun C | fun A fun C | 编译错误 |
大致结论:
场景3,没什么好说的,直接调用“=”复制操作符
场景4,是在复制时发生类型转换的场景。①的结果表明,先调用了输入参数为int的构造函数,然后调用了复制操作符。因为int参数的构造函数是隐式调用的,所以在③里,这个函数加了explicit后,就不灵光了
场景2,是使用int进行复制初始化的场景。从①的结果看,似乎被转化成了对CLASS_TYPE(int dd)的调用,而和CLASS_TYPE(const CLASS_TYPE & bb)无关,但若和CLASS_TYPE(const CLASS_TYPE & bb)真的一点关系都没有,那②就不应该报错。结合②、③来看,场景2应该是先调用CLASS_TYPE(int dd)生成临时对象tmpobj,然后以tmpobj为参数调用CLASS_TYPE(const CLASS_TYPE & bb)初始化objb。但是为什么运行结果①只打印出了fun A呢?估计是生成代码时编译器对这个流程作了优化,把先fun A再fun B的流程优化成了直接调用CLASS_TYPE(int dd)来初始化objb,这样避免了生成临时对象。虽然结果有优化,但编译检查仍然是按照先fun A再fun B的流程来检查的。②、③中分别给funB和funA加了explicit关键字,使得它们不能隐式调用,所以就编译错误了。
场景1,毫无疑问的是,这个场景只调用CLASS_TYPE(const CLASS_TYPE & bb)。但问题是,编译阶段会check几次。很有可能是和场景2一样的原理,但说不准也有可能只check1次。但这已经无关紧要了。