类型转换函数
程序员文章站
2023-11-23 08:01:39
[TOC] 1. 转换构造函数 类的构造函数可以定义不同类型的参数,当参数满足下列条件时,就可称其为转换构造函数。 函数仅有一个参数 参数是基本类型或者其他类类型 其中,有一种特殊情形,也可构成转换构造函数。 函数有多个参数,但除了第一个参数外,其余都是默认参数 第一个参数是基本类型或者其他类类型 ......
目录
1. 转换构造函数
类的构造函数可以定义不同类型的参数,当参数满足下列条件时,就可称其为转换构造函数。
- 函数仅有一个参数
- 参数是基本类型或者其他类类型
其中,有一种特殊情形,也可构成转换构造函数。
- 函数有多个参数,但除了第一个参数外,其余都是默认参数
- 第一个参数是基本类型或者其他类类型
- 函数调用时只使用一个参数
c++编译器在进行编译工作时,会尽力尝试让源码通过编译,因此如果碰到了这样的代码test t = 100
,编译器不会立即报错,而是进行以下尝试:
- 查找类中是否有定义转换构造函数
- 如果定义了test(int i),则先调用test(100)将int类型隐式转换为test类型,再赋值给t,编译通过
- 如果没有定义,编译才报错
#include <iostream> using namespace std; class test { int mvalue; public: test() { mvalue = 0; } //转换构造函数 test(int i) { mvalue = i; } //当仅以第一个参数调用时,该函数等价于test(int i),也是转换构造函数 /*test(int i, int j = 0, int k = 0) { mvalue = i; }*/ test operator + (const test &p) { test ret(mvalue + p.mvalue); return ret; } int value() { return mvalue; } }; int main() { test t = 5; // test t = test(5); test r = t + 10; // test r = t + test(10); cout << "t.value = " << t.value() << endl; cout << "r.value = " << r.value() << endl; return 0; }
可以看到,当定义了转换构造函数时,编译器尽力尝试的结果是隐式类型转换,而隐式类型转换
- 有可能会让程序以意想不到的方式工作
- 是工程中bug的重要来源,应该尽力避免
2. explicit关键字
- 在工程中可以使用explicit关键字修饰转换构造函数,从而杜绝编译器的转换尝试
- 转换构造函数被explicit修饰时只能使用显式的强制类型转换
- 作为编程的一般性原则,建议给所有的构造函数都加上explicit关键字
#include <iostream> using namespace std; class test { int mvalue; public: explicit test() { mvalue = 0; } explicit test(int i) { mvalue = i; } //当仅以第一个参数调用时, 该函数等价于test(int i), 也是转换构造函数, explicit有效且有必要 /*explicit test(int i, int j = 0, int k = 0) { mvalue = i; }*/ test operator + (const test &p) { test ret(mvalue + p.mvalue); return ret; } int value() { return mvalue; } }; int main() { //test t = 5; // error //test r = t + 10; // error test t = static_cast<test>(5); test r = t + static_cast<test>(10); cout << "t.value = " << t.value() << endl; cout << "r.value = " << r.value() << endl; return 0; }
当使用了explicit关键字后,如果main()使用40-41行替换43-44行,编译会直接报错
3. 类型转换函数
转换构造函数可以将其他类型转换为类类型,而类型转换函数则可以将类类型转换到其他类型,包括普通类型和其他类类型。
- 类型转换函数是转换构造函数的逆过程,它们具有同等的地位
- 编译器也能够使用类型转换函数进行隐式转换,从而尽力让源码通过编译
- 当目标类型是其他类类型时,类型转换函数可能与转换构造函数冲突
定义类型转换函数需要用到operator关键字,其语法规则为
operator targettype () { targettype ret; //...... return ret; }
当编译器遇到test t(1); int i = t;
这样的代码时,不会立即报错,而是进行以下尝试
- 查看test类中是否有定义类型转换函数
operator int ()
- 如果有定义,则进行隐式转换,先调用类型转换函数将t转换为int,再赋值给i,编译通过
- 如果没有定义,编译才报错
#include <iostream> using namespace std; class test; class value { int mvalue; public: value(int i = 0) { mvalue = i; } //如果不加explicit,会与test中的operator value ()冲突,产生二义性 explicit value(test &t) { } int value() { return mvalue; } }; class test { private: int mvalue; public: test(int i = 0) { mvalue = i; } int value() { return mvalue; } operator int () { return mvalue; } operator value () { value ret(mvalue); return ret; } }; int main() { test t(100); int i = t; value v = t; cout << "i = " << i << endl; cout << "v.value = " << v.value() << endl; return 0; }
和转换构造函数不同,类型转换函数没有类似explicit这种杜绝机制,也就是说,只要定义了类型转换函数,我们就无法抑制编译器的隐式调用。
因此,在工程中,通常不会使用类型转换函数,而是以totype()的public成员函数来代替类型转换函数。
#include <iostream> using namespace std; class test; class value { int mvalue; public: value(int i = 0) { mvalue = i; } //如果不加explicit,会与test中的operator value ()冲突,产生二义性 explicit value(test &t) { } int value() { return mvalue; } }; class test { private: int mvalue; public: test(int i = 0) { mvalue = i; } int value() { return mvalue; } /* * 工程中不用且不推荐的方式 */ /*operator int () { return mvalue; } operator value () { value ret(mvalue); return ret; }*/ /* * 工程中常用且推荐的方式:提供totype()的public成员函数 */ int toint() { return mvalue; } value tovalue() { value ret(mvalue); return ret; } }; int main() { test t(100); int i = t.toint(); value v = t.tovalue(); cout << "i = " << i << endl; cout << "v.value = " << v.value() << endl; return 0; }