呼和浩特建设厅官方网站,查询企业的软件,房屋设计图平面图,建筑模板分为哪几类目录
1、引言
2、C11 新增关键字详解
2.1、auto
2.2、override
2.3、final
2.4、nullptr
2.5、使用delete阻止拷贝类对象
2.6、decltype
2.7、noexcept
2.8、constexpr
2.9、static_assert VC常用功能开发汇总#xff08;专栏文章列表#xff0c;欢迎订阅#xf…目录
1、引言
2、C11 新增关键字详解
2.1、auto
2.2、override
2.3、final
2.4、nullptr
2.5、使用delete阻止拷贝类对象
2.6、decltype
2.7、noexcept
2.8、constexpr
2.9、static_assert VC常用功能开发汇总专栏文章列表欢迎订阅持续更新...https://blog.csdn.net/chenlycly/article/details/124272585C软件异常排查从入门到精通系列教程专栏文章列表欢迎订阅持续更新...https://blog.csdn.net/chenlycly/article/details/125529931C软件分析工具从入门到精通案例集锦专栏文章正在更新中...https://blog.csdn.net/chenlycly/article/details/131405795C/C基础与进阶专栏文章持续更新中...https://blog.csdn.net/chenlycly/category_11931267.html C11新特性很重要作为C开发人员很有必要去学习不仅笔试面试时会涉及到开源代码中会大规模的使用。以很多视频会议及直播软件都在使用的开源WebRTC项目为例WebRTC代码中大篇幅地使用了C11及以上的新特性要读懂其源码必须要了解这些C的新特性。所以接下来一段时间我将结合工作实践给大家详细讲解一下C11的新特性以供借鉴或参考。
1、引言 为了提升C语言的灵活性和效率C11引入了多个关键字比如auto、overide、final、nullptr、decltype、constexpr、noexcept、static_assert等。本文结合编码实战主要介绍一下C11中引入的常用新关键字。 C11及以上新标准引入了很多新特性使C变得更加灵活但也使得C的特性变得更加臃肿使C变得更加难以驾驭。 2、C11 新增关键字详解
2.1、auto 编程时常常需要将表达式的值赋值给变量这要求在声明变量时清楚地指出表达式的类型但有时很难确定。C11标准引入了auto类型说明符用它能让编译器替我们去分析表达式所属的类型。auto变量必须要初始化因为编译器要通过初始值来推算该auto变量的类型。 自动类型推导用于从初始化表达式中推断出变量的数据类型实际上是在编译时对变量进行了类型推导所以不会对程序的运行效率造成不良影响。示例如下
auto i 2 // int类型
auto d 1.0 // double类型
auto str hello word; // const char*
auto ch A; // char类型
auto func lessint() // 函数指针
vectorint vtList;
auto iter vtList.begin(); // 选代器类型
auto p new foo(); // 自定义类型
2.2、override 派生类在重写基类的虚函数时可以在函数前添加virtaul标记这样我们看到这个标识后就知道该函数是重写基类的虚函数了。在C11引入override关键字之后我们就可以使用override来更明显标识重写的函数。 给派生类的函数添加override标识的好处是一方面使程序员重写基类的意图更加清晰另一方面让编译器发现一些错误如果我们用override标识了派生类的某个函数但该函数没有覆盖基类的虚函数则编译器会报错。
class Base
{
public:virtual void func() const{cout __func__ std::endl;}
}class Derived public Base
{
public:virtual void func() overide{cout __func__ std::endl;}
}
2.3、final 有时我们需要定义这样一种类我们不希望其他类继承它。或者不想考虑它是否适合做一个基类。为了实现这一目的C11引入了一个用来阻止继承的关键字final将该关键字放到类名之后即表示该类不能被继承。示例如下
class Base{ /* */};
class Last final : public Base { /* */}; 还可以用该关键字去修饰一个类的成员函数时来阻止该函数被派生类重写
class Base
{
public:virtual void func() const{cout __func__ std::endl;}
}class Derived public Base
{
public:virtual void func() overide final{cout __func__ std::endl;}
}
2.4、nullptr nullptr是为了解决原来C中NULL的二义性问题而引进的一种新的类型因为NULL实际上代表的是0。最好使用字面值nullptr去初始化一个指针表示当前是空指针。nullptr是一种特殊类型的字面值它可以被转换成任意其他的指针类型。 为什么说nullptr可以解决NULL的二义性呢可以来看个实例
void func( int );
void func( int* );
比如上面的两个重载函数我们要调用func函数如果传入NULLNULL实际上代表的是0可以隐式转换成void*进而转成int*两个函数感觉都能调进去编译器不知道调用的是哪个这样就产生了二义性编译时会报错。而传nullptr参数时编译就不报错了就比较明确了调用的就是void func( int* )。 nullptr_t是变量类型其值就是nullptr可以看看nullptr_t的定义
#ifdef __cplusplusnamespace std{typedef decltype(__nullptr) nullptr_t; // nullptr_t被声明为__nullptr的类型}using ::std::nullptr_t;
#endif
如果函数参数中有nullptr_t时不用指定变量名的直接在函数体中对应的参数设置成nullptr就可以了。以shared_ptr智能指针的某个构造函数为例代码来源Visual C中的智能指针的源码实现
templateclass _Dx,class _Alloc,enable_if_tconjunction_vis_move_constructible_Dx,_Can_call_function_object_Dx, nullptr_t, int 0
shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax) // 参数类型为nullptr_t对应的值就是nullptr
{ // construct with nullptr, deleter, allocatorSetpda(nullptr, _STD move(_Dt), _Ax);
}
2.5、使用delete阻止拷贝类对象 其实这个地方的delete不是关键字 在C11新标准中我们通过将拷贝构造函数和拷贝复制函数定义为删除的函数来阻止拷贝。在函数后面加上delete函数就变成了删除的函数。对于删除的函数我们虽然声明了它但不能用任何方式使用它。 以智能指针类std::unique_ptr为例该智能指针类不支持拷贝构造和赋值操作主要支持所有权移动操作。所以在类的定义中将该类的拷贝构造函数和赋值函数设置为delete函数用来阻止拷贝构造和赋值这是一个经典的面试题std::unique_ptr智能指针是如何禁止拷贝和复制的
unique_ptr(const unique_ptr) delete;
unique_ptr operator(const unique_ptr) delete;
我们不能通过不实现拷贝构造函数和赋值函数去实现这个目标因为当我们不实现这两个函数时编译器会自动帮我们生成默认的拷贝构造函数和赋值函数。 在没有C的delete的标识之前可以将拷贝构造函数和赋值函数设置为private的这样在类外部不能使用这两个函数。
2.6、decltype 有时希望通过表达式的类型推断出要定义的变量的类型但不想用该表达式的值来初始化变量去推断变量的类型为了满足这一要求C11标准引入了decltype类型指示符它的作用是推断操作数的数据类型并返回编译器只分析推断表达式的类型但不去计算表达式的值不对表达式求值。
const int nVal 0;
decltype(nVal) y 1;
decltype(t u) // 其中t和u是数据类型不对表达式tu进行求值只去推断tu表达式的数据类型。 类型说明符生成指定表达式的类型编译时根据表达式的类型去推导出类型示例如下
int i;
struct A
{double x;
}
const A* a new A()decltype(i) x2 // int
dec1type (a-x x3 // double
dec1type((a-x)) x4; // double
2.7、noexcept 在C11标准中提供了noexcept关键字用来指定某个函数不抛出异常。将该关键字放到函数的参数列表之后如下
void func() noexcept; // 这里noexcept作为修饰符 对于用户及编译器来说预先知道某个函数不抛出异常很有好处。首先知道函数不会抛出异常有助于简化调用该函数的代码其次如果编译器确认 函数不抛出异常它就能执行某些特殊的优化操作而这些优化操作并不适用于可能出错的代码。 可能会出现一种情况虽然函数使用noexcept关键字声明不抛出异常但实际上函数内部还是抛出了异常。一旦一个noexcept函数抛出了异常程序就会调用std::terminate()终止程序以确保不在运行时抛出异常的承诺。 指明某个函数不抛出异常这样调用者不必考虑如何处理异常了无论是函数确实不抛出异常还是抛出异常后被强行终止调用者都无需为此负责。 在C98中用throw()来声明不抛出异常throw(异常类型)声明可能抛出的异常类型。noexcept效率比throw更高一些因为编译器可以用std::terminate()来终止程序运行而throw异常机制会有一些额外开销如函数栈依次展开并析构自动变量。 在程序进程的内存不足时new操作会抛出bad_alloc异常内存分配失败这个问题我们以前讲过处理办法是在new时传如一个std::nothrow参数让new在申请不到内存时不要抛出异常直接返回为NULL这样我们就可以通过返回的地址是否为NULL空判断是否是内存申请失败了代码如下
#include iostreamint main(){char *p NULL;int i 0;do{p new(std::nothrow) char[10*1024*1024]; // 每次申请10MBi;Sleep(5);}while(p);if(NULL p){std::cout 分配了 (i-1)*10 M内存 //分配了 1890 Mn内存第 1891 次内存分配失败 第 i 次内存分配失败;}return 0;
}
当然程序进程内存不足时业务没法正常展开和执行了让程序还活着其实也没多大意义了。
2.8、constexpr 指值不会改变并且在编译过程就能得到计算结果的表达式。 C11 新标准规定允许将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式。声明为constexpr 的变量一定是一个常量而且必须用常量表达式初始化。
constexpr int mf 20 // 20 是常量表达式
constexor int limit mf 1; // mf 1 是常量表达式
constexor int sz size(); // 只有当size是一个constexpr函数时才是一条正确的语句 constexpr也可以修饰函数一旦函数声明为constexpr则函数的返回值类型及所有形参的类型都必须是字面值类型而且函数体中有且只能有一条retrun语句。
constexpr int new_sz(){ retrun 20; };
2.9、static_assert static_assert用来做编译期间的断言因此叫做静态断言。语法如下 static_assert(常量表达式提示字符串) 比如
static_assert(sizeof(int) sizeof(unsigned int), int is not smaller than unsigned int);
如果第一个参数常量表达式的值为真(true或者非零值)那么static_assert不做任何事情就像它不存在一样否则会产生一条编译错误错误位置就是该static_assert语句所在行错误提示就是第二个参数提示字符串。 使用static_assert我们可以在编译期间发现更多的错误用编译器来强制保证一些契约并帮助我们改善编译信息的可读性。