3.2 阻止基于转换构造函数进行的隐式类型转换
由上文可知,当定义了一个转换构造函数后,就打通了某个其它类型向类类型进行转换的通道 。此时,如果我们希望禁用基于转换构造函数进行的隐式类型转换,则需要在转换构造函数前追加 explicit 声明 。
当一个转换构造函数被声明为 explicit 后,其具有以下性质:
1. 禁止一切场合下的基于转换构造函数的隐式类型转换
2. 不影响被转换类型到类类型的强制类型转换
3. 不影响对转换构造函数的正常调用
参考以下代码:
struct A { explicit A (int) {} }; // explicit转换构造函数void test(A) {}int main{A _ = 0; // Error!禁止赋值时发生的隐式类型转换!test(0); // Error!禁止实参传递时发生的隐式类型转换!static_cast<A>(0); // explicit不影响强制类型转换A(0); // explicit不影响对转换构造函数的正常调用}上述代码中,我们将类A 的转换构造函数声明为 explicit,则此时 int 将不能通过赋值或实参传递的方式隐式的转换为 A 。但显然,explicit 只是禁用了转换构造函数的隐式类型转换功能,其构造函数功能以及显式类型转换功能并不受影响 。
4.类型转换运算符转换构造函数定义了其它类型向类类型的转换方案,类型转换运算符则定义了与之相反的过程:其用于定义类类型向其它类型的转换方案 。当类定义了某种类型的类型转换运算符后,类类型将可以向被定义类型发生类型转换 。
参考以下代码:
struct A { operator int const { return 0; } }; // 定义A -> int进行类型转换的方案void test(int) {}int main{test(A); // 发生了A -> int的隐式类型转换}与转换构造函数类似,如果希望禁用隐式类型转换,则需要对类型转换运算符追加 explicit 声明 。同样的,explicit 不影响强制类型转换 。
参考以下代码:
struct A { explicit operator int const { return 0; } }; // explicit类型转换运算符void test(int) {}int main{test(A); // Error!禁止A -> int的隐式类型转换test(static_cast<int>(A)); // explicit不影响强制类型转换}对于类型转换运算符与explicit 还有一条额外规定:operator bool 在条件表达式(这主要包括:if、while、for、 ?: 的条件部分)或逻辑表达式中发生的隐式类型转换将不受 explicit 影响 。
参考以下代码:
struct A { explicit operator bool const { return true; } }; // explicit类型转换运算符int main{if (A) {} // 即使operator bool被声明为explicit,其在if中也能发生隐式类型转换}5.继承类到基类的类型转换5.1 静态类型与动态类型C++ 的继承机制决定了这样的抽象模型:继承类 = 基类部分 + 继承类部分 。这意味着每一个继承类都含有其所有基类(如果基类不止一个)的数据各一份 。也就是说,对于一个继承类对象,对其基类部分进行操作显然是可行的,这主要包括:
1. 得到基类部分的数据
2. 将类型转换为基类类型(以丢失某些信息为代价)
也就是说,我们可以将一个继承类对象直接赋值给一个基类类型的变量,显然,这样的赋值建立在隐式类型转换之上,称为继承类到基类的类型转换,或称为向上类型转换 。
根据附加类型的不同,向上类型转换分为以下几种情况:
struct A {};
struct B: A {};
int main
{
A a1 = B; // 值向上转换
A *a2 = new B; // 指针向上转换
A &a3 = a1; // 左值引用向上转换
A &&a4 = B; // 右值引用向上转换
}
上述代码中,变量a1 的类型是 A,这是一个非指针或引用变量,故变量的内存大小就是 A类对象的大小 。如果对基类类型变量使用继承类对象赋值,则将强行去除继承类对象的继承类部分,而将基类部分赋值给变量 。
故对于 a1 而言,其得到的应该是一个 B 类对象的 A 类部分 。即:如果发生向上类型转换的类型是类本身,则将以丢失继承类对象的继承类部分为代价进行向上类型转换 。
事实上,此赋值操作调用了 A 类的合成拷贝赋值运算符,而非基于隐式类型转换 。C++ 对于类的某些成员函数的合成操作是一个非常复杂的话题,且涉及大量与本文无关的内容,故本文不再详述 。
对于变量 a2-4,其类型都是 A 的指针或引用(也是指针),而非 A 的本体 。由于指针本身并不与类型直接挂钩,故理论上,此类变量中真正存放的值可以是一个非 A 类型的数据 。
由此,我们引出“静态类型”与“动态类型”的概念 。
C++ 中,一个变量声明的类型称为静态类型,而其实际存储的数据的类型称为动态类型 。
推荐阅读
- 梦见猴子缠着我甩不掉最后死了 梦见猴子缠着我甩不掉如何甩掉
- 局域网MAC地址认证上网如何实现
- C和C++标准库,这是什么玩意?
- 喝茶前如何洗茶 避免误操作的三大要领
- VLAN工作原理之ACCESS:接收到带VLAN的报文如何处理
- 如何选购与冲泡陈年普洱茶
- 胡萝卜炖排骨汤如何做才好吃?
- 红茶有哪些类别红茶类别如何区分
- 170男生如何穿搭,穿搭显高秘籍都在这里了,快来学习吧
- 阿里巴巴网店怎么开啊流程是怎样 如何阿里巴巴开店步骤