构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用
一、单参构造函数
- 还是老朋友,我们通过下面这个日期类进行讲解
class Date { public: Date(int year) :_year(year) {} private: int _year; int _month = 3; int _day = 31; };
对于下面的d1很清楚一定是调用了有参构造进行初始化,不过对于d2来说,也是一种构造方式
int main() { Date d1(2022); Date d2 = 2023; return 0; }
依旧通过调试来看就会非常清晰,这种写法也会去调用构造函数
在操作符章节,我有提到过【隐式类型转换】这个概念,像下面将一个int类型的数值赋值给到一个double类型的数据,此时就会产生一个隐式类型转换
int i = 1; double d = i;
- 对于类型转换而言,我在C++引用一文中也有提到过,这里并不是将值直接赋值给到左边的对象,而是在中间呢会产生一个临时变量,例如右边的这个
i
会先去构造一个临时变量,这个临时变量的类型是[double]
。把它里面的值初始化为1,然后再通过这个临时对象进行拷贝构造给d
,这就是编译器会做的一件事 - 那对于这个d2其实也是一样,2023会先去构造一个临时对象,这个临时对象的类型是
[Date]
把它里面的year初始化为2023,然后再通过这个临时对象进行拷贝构造给到d2
,
小陈:不是说构造函数有初始化列表吗?拷贝构造怎么去初始化呢?
//拷贝构造 Date(const Date& d) :_year(d._year) ,_month(d._month) ,_day(d._day) {}
同学,别忘了【拷贝构造】也是属于构造函数的一种哦,也是会有初始化列表的
刚才说到了中间会产生一个临时对象,而且会调用构造 + 拷贝构造,那此时我们在Date类中写一个拷贝构造函数,调试再去看看会不会去进行调用
- 很明显没有,我在进入Date类后一直在按F11,但是却进不到拷贝构造中,这是为什么呢?
- 原因其实在于编译器在这里地方做了一个优化,将【构造 + 拷贝构造】优化成了【一个构造】,因为编译器在这里觉得构造再加拷贝构造太费事了,干脆就合二为一了。其实对于这里的优化不同编译器是有区别的,像一下VC++、DevC++可能就不会去优化,越是新的编译器越可能去进行这种优化。在本文的最后一个模块我还会详细展开分析
小叶:但您是怎么知道中间赋值这一块产生了临时对象呢?如果不清楚编译器的优化机制这一块肯定就会认为这里只有一个构造
- 这点确实是,若是我现在不是直接赋值了,而是去做一个引用,此时会发生什么呢?
Date& d3 = 2024;
可以看到,报出了一个错误,原因就在于d3是一个Date类型,2024则是一个内置类型的数据
- 但若是我在前面加一个
const
做修饰后,就不会出现问题了,这是为什么呢?
其实这里的真正原因就在于产生的这个【临时变量】,它就是通过Date类的构造函数构造出来的,同类型之间可以做引用。还有一点就是临时变量具有常性,所以给到一个const
类型修饰对象不会有问题
但若是你不想让这种隐式类型转换发生怎么办呢?此时就可以使用到C++中的一个关键字叫做
explicit
- 它加在构造函数的前面进行修饰,有了它就不会发生上面的这一系列事儿了,它会【禁止类型转换】
explicit Date(int year) :_year(year) {}
二、多参构造函数
对于上面所讲的都是基于单参的构造函数,接下去我们来瞧瞧多参的构造函数
//多参构造函数 Date(int year, int month ,int day = 31) :_year(year) ,_month(month) ,_day(day) {}
- 根据从右往左缺省的规则,我们在初始化构造的时候要给到2个参数,
d1
没有问题传入了两个参数,但是若是像上面那样沿袭单参构造函数这么去初始化还行得通吗?很明显不行,编译器报出了错误
小冯:那要怎么办呀,对于一定要传入多参数进行构造的场景
这个时候就要使用到我们C++11中的新特性了,在对多参构造进行初始化的时候在外面加上一个{}
就可以了,可能你觉得这种写法像是C语言里面结构体的初始化,但实际不是,而是在调用多参构造函数
Date d2 = { 2023, 3 };
- 不仅如此,对于下面这种也同样适用,调用构造去产生一个临时对象
const Date& d3 = { 2024, 4 };
那要如何去防止这样的隐式类型转换发生呢,还是可以使用到
explicit
关键字吗?
//多参构造函数 explicit Date(int year, int month ,int day = 31) :_year(year) ,_month(month) ,_day(day) {}
- 可以看到,加上
explicit
关键字做修饰,同样可以起到【禁止类型转换】的作用
- 还有一种例外,当缺省参数从右往左给到两个的时候,此时只需要传入一个实参即可,那也就相当于是单参构造
explicit
关键字依旧可以起到作用·
explicit Date(int year, int month = 3,int day = 31) :_year(year) ,_month(month) ,_day(day) {}
所以对于可读性不是很好的代码,可以使用explicit
修饰构造函数,将会禁止构造函数的隐式转换
以上就是C++ 之explicit关键字的详细内容,更多关于explicit关键字的资料请关注脚本之家其它相关文章!