c++11 类中关于default、explict、implicit、noexcept、final的详解

default

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了,用来限定一些方法,如基类指向子类指针时,如果子类重写了(即同名函数)该方法,那么父类会优先调用子类,这样的话就是限定子类的某些行为,达到使用父类指针指向子类这样的多态性写法时,永远只能调用父类的这个方法。

到此这篇关于c++11 类中关于default、explict、implicit、noexcept、final的详解的文章就介绍到这了,更多相关c++11 类default、explict、implicit、noexcept、final内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++11中的default函数使用

    对于C++ 11标准中支持的default函数,编译器会为其自动生成默认的函数定义体,从而获得更高的代码执行效率,也可免除程序员手动定义该函数的工作量. C++的类有四类特殊成员函数,它们分别是: 默认构造函数 析构函数 拷贝构造函数 拷贝赋值运算符 这些类的特殊成员函数负责创建.初始化.销毁,或者拷贝类的对象,如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数.例如: 清单 1 class X{ private:

  • C++11新特性“=default”,“=delete”的使用

    1. =default 和=delete 概述 任何事物的出现都必然有着其出现的理由,伴随着每一个新的概念产生都会带来一系列的便利和价值.C++在不断的演变与发展,与此同时,伴随着许多新的特性和功能产生.=default.=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本).C++11中引进这两种新特性的目的是为了增强对"类默认函数的控制",从而让程序员更加精准地去控制默认版本的函数.其具体的功能和使

  • c++11 类中关于default、explict、implicit、noexcept、final的详解

    default default是c++11的标准,它的作用是告诉编译器声明一个无参的默认构造函数. 最初的时候我们声明类是这样的: class test{ public: int add(){} }; 由于我们没有给默认构造函数,c++编译器隐式的帮我们增加了一个默认的无参构造函数,注意这一点取决于编译,有的编译器不会增加,但大多数都会,如GCC.MSVC. 但是一旦我们声明了一个有参的构造函数: class test{ public: test(int a){} int add(){} };

  • php类中的$this,static,final,const,self这几个关键字使用方法

    本篇文章主要分项了一下关于php类中的$this,static,final,const,self这几个关键字使用方法. $this $this表示当前实例,在类的内部方法访问未声明为const及static的属性时,使用$this->value='phpernote';的形式.常见用法如: $this->属性 $this->方法 举例如下: <?php class MyClass{ private $name; public function __construct($name){

  • JSP 中Spring组合注解与元注解实例详解

    JSP 中Spring组合注解与元注解实例详解 摘要: 注解(Annotation),也叫元数据.一种代码级别的说明.它与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明 1. 可以注解到别的注解上的注解称为元注解,被注解的注解称为组合注解,通过组合注解可以很好的简化好多重复性的注解操作 2. 示例组合注解 import org.springframework.context.annotation.ComponentScan; im

  • Java中的引用和动态代理的实现详解

    我们知道,动态代理(这里指JDK的动态代理)与静态代理的区别在于,其真实的代理类是动态生成的.但具体是怎么生成,生成的代理类包含了哪些内容,以什么形式存在,它为什么一定要以接口为基础? 如果去看动态代理的源代码(java.lang.reflect.Proxy),会发现其原理很简单(真正二进制类文件的生成是在本地方法中完成,源代码中没有),但其中用到了一个缓冲类java.lang.reflect.WeakCache<ClassLoader,Class<?>[],Class<?>

  • 基于Spring中各个jar包的作用及依赖(详解)

    先附spring各版本jar包下载链接http://repo.spring.io/release/org/springframework/spring/ spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2.jar 示例图片为Spring-2.5.6.jar的包目录 下面讲解各个jar包的作用: 1.org.springframework.aop或sp

  • pytorch中的卷积和池化计算方式详解

    TensorFlow里面的padding只有两个选项也就是valid和same pytorch里面的padding么有这两个选项,它是数字0,1,2,3等等,默认是0 所以输出的h和w的计算方式也是稍微有一点点不同的:tf中的输出大小是和原来的大小成倍数关系,不能任意的输出大小:而nn输出大小可以通过padding进行改变 nn里面的卷积操作或者是池化操作的H和W部分都是一样的计算公式:H和W的计算 class torch.nn.MaxPool2d(kernel_size, stride=Non

  • Android Studio 3.6中新的视图绑定工具ViewBinding 用法详解

    前言 我们在Android开发的过程中总是需要获取XML布局中的ViewId,以便给其赋值进行显示,早期我们只能使用 findViewById 这个API,会导致很多的模版代码出现.2013年左右Android界大神 Jake Wharton开源了Butter Knife框架,通过Bind("viewid")方式方便开发者获取ViewId.近两年由于谷歌对Kotlin的支持,我们开始使用 Android Kotlin extensions. 在文件中导入布局文件直接引用viewId.无

  • vue中的v-model原理,与组件自定义v-model详解

    VUE中的v-model可以实现双向绑定,但是原理是什么呢?往下看看吧 根据官方文档的解释,v-model其实是一个语法糖,它会自动的在元素或者组件上面解析为 :value="" 和 @input="", 就像下面这样 // 标准写法 <input v-model="name"> // 等价于 <input :value="name" @input="name = $event.target.val

  • java中常见的6种线程池示例详解

    之前我们介绍了线程池的四种拒绝策略,了解了线程池参数的含义,那么今天我们来聊聊Java 中常见的几种线程池,以及在jdk7 加入的 ForkJoin 新型线程池 首先我们列出Java 中的六种线程池如下 线程池名称 描述 FixedThreadPool 核心线程数与最大线程数相同 SingleThreadExecutor 一个线程的线程池 CachedThreadPool 核心线程为0,最大线程数为Integer. MAX_VALUE ScheduledThreadPool 指定核心线程数的定时

  • java中的抽象类和接口定义与用法详解

    目录 一.抽象类 1.什么叫抽象类? 2.抽象类的特点: 3.成员特点: 二.接口 1.接口是什么? 2.接口的特点 3.接口的组成成员 4.类与抽象的关系: 5.抽象类与接口的区别: 一.抽象类 1.什么叫抽象类? 例如在生活中我们都把狗和猫归为动物着一类中,但当只说动物时,我们是不知道是猫还是狗还是其他的.所以动物就是所谓的抽象类,猫和狗则是具体的类了.因此在Java中,一个没有方法体的方法应该定义为抽象类,而类中有抽象方法,则必须为抽象类. 2.抽象类的特点: 抽象类与抽象方法必须用abs

随机推荐