C++ 11 关键字
1.auto
我现在用auto,基本是在变量定义时根据初始化表达式自动推断该变量的类型。
另外与关键字 decltype 连用,在声明或定义函数时作为函数返回值的占位符。
auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。
在下面这个例子中,函数返回值类型是 operator+ 操作符作用在T、U类型变量上的返回值类型。
template<class T, class U> auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
2.using
C++ 11 中 使用using来代替typedef来命名,更加具有可读性
using uint8=unsigned char;
//等价于typedef unsigned char uint8;
using FunctionPtr = void (*)();
//等价于typedef void (*FunctionPtr)();
template<typename T> using MapString = std::map<T, char*>;
//定义模板别名,注意typedef无法定义模板别名,因为typedef只能作用于具体类型而非模板
C++ 11之前 主要用于名字空间、类型、函数与对象的引入,实际上是去除作用域的限制
//引入名字空间
using namespace std;
//引入类型
using std::iostream;
//引入函数
using std::to_string;
//引入对象
using std::cout;
//在子类中解除父类同名函数的隐藏
class Base
{
public:
void func()
{
cout << "in Base::func()" << endl;
}
void func(int n)
{
cout << "in Base::func(int)" << endl;
}
}; class Sub : public Base
{
public:
using Base::func; //引入父类所有同名函数func,解除函数隐藏
void func()
{
cout<<"in Sub::func()"<<endl;
}
}; int main()
{
Sub s;
s.func();
s.func(1); // Success!
}
3.decltype
同作为适应C++模板和泛型编程需求而出现的类型推导的关键字,与auto不同的是,decltype总是以一个普通表达式作为参数,返回该表达式的类型,并且不会对表达式进行求值。其规则:
const A* a = new A{0}; //第一种情况:
//如果 e 是一个变量或者类成员访问表达式,假设e的类型是T,那么的decltype(e)为T,decltype((e))为T&
decltype(a->x) y; // type of y is double
decltype((a->x)) z = y; // type of z is const double&,因为a一个常量对象指针 //第二种情况
//如果e是一个解引用操作,那么decltype(e)和decltype((e))均为T&
int* aa=new int;
decltype(*aa) y=*aa; //type of y is int&,解引用操作 //第三种情况
//decltype(e)与decltype((e))均为T。struct A { double x; };
decltype(5) y; //type of y is int
decltype((5)) y; //type of y is int
const int&& RvalRef() { return 1; }
decltype ((RvalRef())) var = 1; //type of var is const int&&
4.nullptr_t 和 nullptr
之前会用0来表示空指针,但存在0被隐式转换成整型的可能,可能引发一些问题。
关键字nullptr是std::nullptr_t类型的值,可以用来指代空指针常量。
nullptr 和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为 bool 型(取值为false),但是不存在上述到整型的隐式转换。
在使用 nullptr_t 与 nullptr 时,注意以下几点:
(1)可以使用nullptr_t定义空指针,但所有定义为nullptr_t类型的对象行为上是完全一致的;
(2)nullptr_t 类型对象可以隐式转换为任意一个指针类型;
(3)nullptr_t 类型对象不能转换为非指针类型,即使使用reinterpret_cast进行强制类型转换也不行;
(4)nullptr_t 类型对象不能用于算术运算表达式;
(5)nullptr_t 类型对象可以用于关系运算表达式,但仅能与 nullptr_t 类型或指针类型对象进行比较,当且仅当关系运算符为==、>=、<=时,如果相等则返回 true。
5.const constexpr
const用来申明常量表达式,可作用于 函数返回值、函数参数、数据申明以及类的构造函数等。
常量表达式指值不会改变并且在编译时期就得到计算结果的表达式
const int i=3; //i是一个常变量
const int j=i+1; //j是一个常变量,i+1是一个常量表达式
int k=23; //k的值可以改变,从而不是一个常变量
const int m=f(); //m不是常变量,m的值只有在运行时才会获取
constexpr
//常量表达式函数
//函数必须有返回值;
//函数体只有单一的return语句;
//return语句中的表达式也必须是一个常量表达式;
//函数在使用前必须已有定义。
constexpr int f(){return 1;} //常量表达式值
constexpr int i=3; //i是一个常变量
constexpr int j=i+1; //i+1是一个常变量
constexpr int k=f(); //只有f()是一个constexpr函数时,k才是一个常量表达式 const int *p=nullptr; //p是一个指向整型常量的指针(pointer to const)
constexpr int *p1=nullptr; //p1是一个常量指针(const pointer) //自定义类型的构造函数
//构造函数体必须为空;
//初始化列表只能使用常量表达式。
struct MyType
{
int i;
constexpr MyType(int x):i(x){}
};
constexpr MyType myType(1); //作用于函数模板
//由于函数模板参数的不确定性,实例化后的模板函数可能不满足常量表达式的条件,此时,C++11标准规定,自动忽略constexpr。 struct NotConstType
{
int i;
NotConstType(int x) :i(x) {}
};
NotConstType myType; //constexpr作用于函数模板
template <typename T> constexpr T ConstExpFunc(T t)
{
return t;
} int main()
{
NotConstType objTmp = ConstExpFunc(myType); //编译通过,ConstExpFunc实例化为普通函数,constexpr被忽略
constexpr NotConstType objTmp1 = ConstExpFunc(myType); //编译失败
constexpr int a = ConstExpFunc(1); //编译通过,ConstExpFunc实例化为常量表达式函数
} //constexpr元编程
//作用于递归函数来实现编译时期的数值计算 C++11标准规定,常量表达式应至少支持512层递归
constexpr int Fibonacci(int n)
{
return (n == 1) ? 1 : (n == 2 ? 1 : Fibonacci(n - 1) + Fibonacci(n - 2));
} int main()
{
constexpr int fib8 = Fibonacci(8); //编译期常量等于21
}
对比
const可以修饰函数参数、函数返回值、函数本身、类等,在不同的使用场景下,const具有不同的意义,不过大多数情况下,const描述的是“运行时常量性”,即在运行时数据具有不可更改性
constexpr可以修饰函数参数、函数返回值、变量、类的构造函数、函数模板等,是一种比const更加严格的约束,它修饰的表达式除了具有“运行时常量性”,也具有“编译时常量性”,即constexpr修饰的表达式的值在编译期间可知。
6.noexcept
这一个关于异常声明exception specification的功能,用于指定函数可能抛出的异常类型。C++11前,在函数声明中会使用throw;C++11中引入noexcept。
voidFunc0() throw(runtime_error);
//可能抛出runtime_error类型的异常
voidFunc1() throw();
//不会抛出任何异常
voidFunc2();
//没有异常说明,可以抛出任何类型的异常
如果函数抛出了没有在异常说明中列出的异常,则编译器会调用标准库函数unexpected。默认情况下,unexpected函数会调用terminate函数终止程序。
- noexcept作为修饰符
void Func() noexcept;
//此时同上述的throw(),表示函数不会抛出异常,也就是说如果发生了,编译器会直接调用std::terminate()终止程序运行。
//同时就效率讨论,noexcept会较throw高一些
void Func() noexcept(常量表达式);
//如果常量表达式的结果为true,表示该函数不会抛出异常;反之抛出。 不规定表达式则默认为true;
- noexcept作为操作符
//第二个 noexcept 是一个操作符,如果其参数是一个有可能抛出异常的表达式,
//noexcept(T()) 则返回值为false,那么 func5 有可能会抛出异常
//否则,noexcept(T()) 返回true,func5 不会抛出异常。这样函数模板是否会抛出异常,可以由表达式进行推导 template <typename T>
void fun() noexcept(noexcept(T())) { throw 1; } class Base
{
public:
virtual void f() {}
};
class Test :public Base
{
public:
~Test() noexcept(true) {}
};
class TestFalse :public Base
{
public:
~TestFalse() noexcept(false) {}
}; int main(int argc, char **argv)
{
std::cout << noexcept(TestFalse()) << std::endl; // false
std::cout << noexcept(Test()) << std::endl; // true try
{
fun<TestFalse>();
}
catch (...)
{
std::cout << "throw" << std::endl; // throw
} try
{
fun<Test>(); // terminate
}
catch (...)
{
std::cout << "throw" << std::endl;
}
return 0;
}
7. final和override
final修饰类或虚函数,表明终结,不能再子类化或者在子类中不能被重写。
override用于辅助检查是否真正重写了继承的虚函数。
注意:override 和 virtual 一样,只能在类体内申明或重写虚函数时使用,在类体外重写虚函数时使用 override 和 virtual 将无法通过编译。
8.sizeof
sizeof… 运算符的作用是获取 C++11 中可变参数模板中参数包中元素个数。类似 sizeof,sizeof… 返回一个常量表达式,而且不会对模板的实参求值
template<typename... Args> void g(Args... args)
{
cout<<sizeof...(Args)<<endl; //类型参数的数目
cout<<sizeof...(args)<<endl; //函数参数的数目
}
9.default delete
C++98和C++03编译器在类中会隐式地产生四个函数:默认构造函数、拷贝构造函数、析构函数和赋值运算符函数,它们被称为特殊成员函数。在 C++11 中,被称为 “特殊成员函数” 的还有两个:移动构造函数和移动赋值运算符函数。如果用户申明了上面六种函数,编译器则不会隐式产生。C++引入的default关键字,可显示地、强制地要求编译器为我们生成默认版本。
class DataOnly
{
public:
DataOnly()=default; //default constructor
~DataOnly()=default; //destructor DataOnly(const DataOnly& rhs)=default; //copy constructor
DataOnly& operator=(const DataOnly & rhs)=default; //copy assignment operator DataOnly(const DataOnly && rhs)=default; //C++11,move constructor
DataOnly& operator=(DataOnly && rhs)=default; //C++11,move assignment operator
};
delete在C++11前用作对象释放运算符,在C++11中获得了新的功能:
//禁止生成默认版本
class DataOnly
{
public:
DataOnly()=delete; //default constructor
~DataOnly()=delete; //destructor DataOnly(const DataOnly& rhs)=delete; //copy constructor
DataOnly& operator=(const DataOnly & rhs)=delete; //copy assignment operator DataOnly(const DataOnly && rhs)=delete; //C++11,move constructor
DataOnly& operator=(DataOnly && rhs)=delete; //C++11,move assignment operator
}; //过滤函数的形参类型
bool isLucky(int number); // original function
bool isLucky(char) = delete; // reject chars
bool isLucky(bool) = delete; // reject bools
bool isLucky(double) = delete; // reject doubles and floats //在模板特例化中,也可以用 delete 来过滤一些特定的形参类型
class Widget
{
public:
template<typename T> void processPointer(T* ptr){}
};
template<> void Widget::processPointer<void>(void*)=delete; //deleted function template
10.static_assert
static_asset静态断言,用于检测和诊断编译器错误。
assert运行时断言宏
static_assert(断言表达式,提示字符串);
static_assert和type traits一起使用能发挥更大的威力。type traits是一些类模板,在编译时提供关于类型的信息,在头文件<type_traits>中可以找到它们。这个头文件中有好几种类模板,有helper class,用来产生编译时常量,有type traits class,用来在编译时获取类型信息,还有就是type transformation class,他们可以将已存在的类型变换为新的类型。下面这段代码原本期望只作用于整数类型
template <typename T1, typename T2> auto add(T1 t1, T2 t2)
{
return t1 + t2;
}
使用static_assert,应当注意:
(1)static_assert可以用在全局作用域,命名空间,类作用域,函数作用域,几乎可以不受限制地使用;
(2)static_assert可以在帮助我们在编译期间发现更多的错误,用编译器来强制保证一些契约,改善编译信息的可读性,尤其是用于模板的时候;
(3)编译器在遇到一个static_assert语句时,通常立刻将其第一个参数作为常量表达式进行演算。如果第一个常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行演算,这就让检查模板参数成为了可能;
(4)由于是static_assert编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失
C++ 11 关键字的更多相关文章
- C++11 关键字 const 到底怎么用?
Const 的作用及历史 const (computer programming) - Wikipedia 一.历史 按理来说,要想了解一件事物提出的原因,最好的办法就是去寻找当时的历史背景,以及围绕 ...
- 【转】 C语言深度解剖读书笔记(1.关键字的秘密)
本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...
- 《C语言深度解剖》学习笔记之关键字
第一章 关键字 C语言共有32个关键字. 关键字 auto 声明自动变量 int 声明整型变量 long 声明长整型变量 char 声明字符型变量 float 声明浮点型变量 short 声明短整 ...
- C++ Pirmer : 第十五章 : 面向对象程序设计之基类和派生的定义、类型转换与继承与虚函数
基类和派生类的定义以及虚函数 基类Quote的定义: classs Quote { public: Quote() = default; Quote(cosnt std::string& bo ...
- javaSE第七天
第七天 36 1. 成员变量和局部变量的区别(理解) 36 (1)在类中的位置不同 36 (2)在内存中的位置不同 36 (3)生命周期不同 36 (4)初始化值不同 ...
- c++面试遇到问题
1. C 和 C++ 区别 2. const 有什么用途 主要有三点: 1:定义只读变量,即常量 2:修饰函数的参数和函数的返回值 3: 修饰函数的定义体,这里的函数为类的成员函数, ...
- Inside The C++ Object Model(一)
============================================================================1-1. 关键字struct和class的一点区 ...
- python入门知识点(上)
1.硬件系统: 主机部分: 1.中央处理器(CPU): 电脑的大脑 运算器: 数值计算和逻辑判断 控制器: 可以电脑中的各个部件协同工作 2.内部存储器: 随机存储器:内存条 使用电信号表示数据; 特 ...
- 数据结构与算法之PHP查找算法(哈希查找)
一.哈希查找的定义 提起哈希,我第一印象就是PHP里的关联数组,它是由一组key/value的键值对组成的集合,应用了散列技术. 哈希表的定义如下: 哈希表(Hash table,也叫散列表),是根据 ...
随机推荐
- Linux信号与golang中的捕获处理
什么是信号 在计算机科学中,信号是Unix.类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式.它是一种异步的通知机制,用来提醒进程一个事件已经发生. 当一个信号发送给一个进程, ...
- [Python] 基本概念
1.基本概念 三大特性:封装.继承.多态 继承:派生类继承基类的字段和方法 多态:对不同类型的变量进行相同的操作,却表现出不同的行为(例如对数字和字符做"+"运算) 封装:将数据和 ...
- cent 7 识别exfat
cent 7 识别exfat install fuse-exfat exfat-utils
- 如何在CentOS 7上安装Htop
在本教程中,我们将向您介绍如何在CentOS 7服务器上安装和配置Htop.对于那些不知道的人,Htop 是为Linux编写的一个交互式实时系统监视进程查看器.它被设计为替代Unix程序的顶部.它显示 ...
- Canal和Otter讨论二(原理与实践)
上次留下的问题 问题一: 跨公网部署Otter 参考架构图 解析 a. 数据涉及网络传输,S/E/T/L几个阶段会分散在2个或者更多Node节点上,多个Node之间通过zookeeper进行协同工 ...
- HTML中option的单页调用
我们在用到下拉列表框select时,需要对选中的<option>选项触发事件,其实<option>本身没有触发事件方法,我们只有在select里的 onchange方法里触发. ...
- xtrabackup(innobackupex)使用详解
innobackupex实际上是percona-xtrabackup的perl整合脚本,功能当然更强大一些. xtrabackup备份实际上是在线的物理热备,为什么和么说呢,因为实际上他是以拷贝mys ...
- 整数划分——区间dp(石子合并)
这不是将一个数以一来划分,而是把一个整数以位来划分 题目描述 如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大.N.M从键盘输入,输出最大值及一种划分方式 ...
- 【Azure Developer】使用 Python SDK连接Azure Storage Account, 计算Blob大小代码示例
问题描述 在微软云环境中,使用python SDK连接存储账号(Storage Account)需要计算Blob大小?虽然Azure提供了一个专用工具Azure Storage Explorer可以统 ...
- FPGA最全科普总结
FPGA最全科普总结 FPGA 是可以先购买再设计的"万能"芯片.FPGA (Field Programmable Gate Array)现场可编程门阵列,是在硅片上预先设计实 ...