c++11 类default、explict、implicit、noexcept、final c++11 类中关于default、explict、implicit、noexcept、final的详解
17岁boy想当攻城狮 人气:0default
default是c++11的标准,它的作用是告诉编译器声明一个无参的默认构造函数。
最初的时候我们声明类是这样的:
class test{ public: int add(){} };
由于我们没有给默认构造函数,c++编译器隐式的帮我们增加了一个默认的无参构造函数,注意这一点取决于编译,有的编译器不会增加,但大多数都会,如GCC、MSVC。
但是一旦我们声明了一个有参的构造函数:
class test{ public: test(int a){} int add(){} };
那么编译器就不会为我们提供默认的无参构造函数了,就会在声明变量时必须传入参数了。
所以诞生了default关键字,只需要在无参的构造函数后面加上它就可以了
class test{ public: test() = default; test(int a){} int add(){} };
那么问题来了,它和我们手动声明无参构造函数有什么区别?
区别一:当使用多文件编程时,使用default声明的构造函数不需要在写实现
区别二:代码执行效率,当我们使用这个关键字定义的构造函数,在声明变量时,编译器不会去调用构造函数,也不会生成构造函数的代码,这点是重点,高效率提高声明变量的时间,如果用户自己声明了构造函数会造成编译器开辟完内存后会去调用一次构造函数。
explict
这个关键字的作用是用于修饰只有一个参数的构造函数,并要求为显示的
那么显示的是什么意思呢?为什么只能修饰一个构造函数呢?
首先我们来看这段代码
class test{ public: test(int a){} }; int main(){ test a(0); test b = 2; }
大家可以看到上面用了两种方式的初始化,一种是(),还有一种是=号,注意这里讲一下区别在哪,()构造会直接调用最匹配的构造函数,并且不会发送隐式转换,如果用=号则编译器需要推导,推导=号右边是一个什么类型,然后去选择与这个类型匹配的构造函数
但是也可能产生一种问题:
class test{ public: test(int a){} test(char a){} }; int main(){ test a(0); test b = 2; }
这里增加了一个参数char的构造函数,那么这个时候可能产生一种问题,就是char是可以用来表示整数的,而2又符合char能表示的范围,所以这里就可能产生了隐式转换,将2转换为了char类型,我们用户甚至可以手动强转,如果编译器够聪明的话会选择正确的构造函数,如果不够聪明呢?
所以为了解决可能产生的这种问题就推出了:explict关键字,用这个关键字声明的构造函数是不允许用户去做可能产生隐式转换的事情
class test{ public: explict test(int a){} test(char a){} }; int main(){ test a(0); test b = 2; //这一行会报错,因为可能会发生隐式转换 //test b = '2' //这样也不行的,因为会优先匹配具有explict的构造函数,那么这样就产生了隐式转换,因为'2'可以被转换成ascii码 }
因为有了explict关键字的存在任何可能发生隐式转换的动作都会被编译器报错,但是如果你用()来调用就没事的
test b('c') ;
因为()会明确表示入参类型,=号的话编译器是需要推导=号左右两边类型,在去调用最合适的构造函数,那么这个时候就产生了可能发生隐式转换的问题。
同时=号初始化也会拖累编译速度,最后明确一点,就是顺序,当调用时编译器会优先匹配explict的构造函数,就如刚刚的test b = '2',已经明确是char符号了,但是编译器仍然认为它可能会出现隐式转换,因为使用explict关键字后你做的任何可能产生类型转换的操作都会被编译器优先裁决。
最后在说明一点就是为什么只能用于只有一个参数的构造函数,为什么不能是多个?
答:因为多个的情况下是无法明确类型的,如果参数有两个或两个以上的情况下,编译器这样是不好推断的,因为两个变量可能是不同的类型,如果是两个不同的类型,那就不能做类型限定,其次类型较多的情况下对于编译器来说也是一种负担。
explict的意义就是在于针对一个变量的构造函数时方式那一个参数类型出现隐式转换,这个是与开发者们有关,最初开发者们写了多个只有一个参数的构造函数时,有时会发生隐式转换导致调不到理想的构造函数,但是多个参数的构造函数因为类型会更明确一点,所以不会出现这样的问题。
implicit
这个关键字其实根explict是相反的,它其实不存在于c++,只在java和c#这样继承c++特性的语言里存在,它表明的是隐藏的,就是表明构造函数可以被隐式转换,只是后来人们把没有使用explict声明的只有一个参数的构造函数都认为隐式带了一个implicit,不知道是谁提的,就挺离谱的,java和c#明明是继承c++,但是后来人们全把这个类型隐式加到c++中。
noexcept
这是c++11增加的函数,目的是为了提升函数效率,即告诉编译器这个函数不会产生异常。
首先开发者们在给函数加上这个关键字时应明确,你的这个函数不会出现任何问题
class test{ public: explict test(int a)noexcept {} };
这里异常的意思是指:段错误和任何可能引起程序崩溃的代码。
c++里有一套机制,就是c++好像对系统层的某些异常做了捕获,当我们使用std::string,在初始化时传入一个NULL时会导致段错误,然后系统会杀死程序,但我发现在杀死之前会先去调用c++的std::terminate(),然后这个函数内部调用std::abort()来杀死我们的程序,在Linux中有消息事件可以完成这个操作。
所以我认为这个关键字的作用就是告诉编译器不要对这个函数做监听,这样就可以提升函数的执行效率,否则当调用这个函数时c++还要去做一些事件监听的注册功能,因为如果一开始全都监听的话c++也不知道你什么时候才会调用,所以最合适的是调用的时候监听。
那么这样的话就提升了函数调用时的一个速度。
final
这个关键字很容易理解,它就是声明这个类不能被继承。
class test final{ }; //这行会报错,因为test不能被继承 class test_son : public test{ }
那么还有一个用处,就是用在虚函数上,表示不能被重写
class test{ public: vritual int add(int a) final{} };
子类继承以后就不能重写test虚函数add了,用来限定一些方法,如基类指向子类指针时,如果子类重写了(即同名函数)该方法,那么父类会优先调用子类,这样的话就是限定子类的某些行为,达到使用父类指针指向子类这样的多态性写法时,永远只能调用父类的这个方法。
加载全部内容