c++11的一些新特(持续补充)
1. auto关键字
在c语言中,auto用于修饰局部变量,也称之为自动变量:
void func()
{
auto int a; //等价于int a
}
在c++11中,auto根据用户的初始化内容自动推导其类型:
#include <iostream>
#include <string>
#include <vector>
struct mt {
int a;
std::string str;
};
char mfunc()
{
return 'v';
}
void mfunc2(std::vector<int>& vec)
{
//auto自动推导容器模板的迭代器
for (auto iter = vec.begin(); iter < vec.end(); ++iter) {
std::cout << *iter << " ";
}
std::cout << std::endl;
}
int main(void)
{
auto a = 11; //auto自动推导a为int类型
auto pa = &a; //auto自动推导pa为int类型的指针
std::cout << "*pa = " << *pa << std::endl;
mt t1;
auto t2 = t1; //auto自动推导t2为自定义类型mt
auto r = mfunc(); //r为char
std::vector<int> vec;
for (auto i = 0; i < 10; i++) {
vec.push_back(i);
}
mfunc2(vec);
return 0;
}
注意:
(1)用auto定义变量时必须使用初始化(而不是赋值),因为auto就是根据初始值内容来分配内存空间的;
(2)auto变量不能作为自定义类型的成员变量:
struct mt1 {
auto i;
};
(3)不能定义auto数组:
auto marr[2] = {1, 2};
(4)模板实例化类型不能是auto类型:
std::vector<auto> vec1 = {1, 2, 3};
(5)函数形参不可以是auto变量
void mfunc3(auto i)
{
}
2. decltype关键字
decltype可通过一个变量获取其类型,这样我们自定义的匿名类型在声明之后还可以继续用来定义变量:
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
struct { /* 这是匿名结构体类型 */
int a;
} ms;
decltype(ms) ms2; /* 获取ms的类型后定义ms变量 */
int a;
/* 打印类型的名字,这个在不同编译器有不同的表达 */
cout << typeid(a).name() << endl;
cout << typeid(ms2).name() << endl;
return 0;
}
3. 追踪函数返回值类型
用auto关键字声明函数的返回值类型,需要用”->”指定具体是什么类型:
auto mfunc(int x, int y)->int
{
return x + y;
}
利用decltype代替代码中的int:
auto mfunc(int x, int y)->decltype(x + y)
{
return x + y;
}
这即是追踪函数返回值类型,将其应用于泛型编程中:
template<typename T1, typename T2>
auto mfunc1(T1 t1, T2 t2)->decltype(t1 * t2)
{
return t1 * t2;
}
因为t1、t2是泛指类型,所以程序员无法为mfunc2()指定二者相乘的类型,利用函数返回值追踪就很容易实现。
4. 直接初始化类中的成员变量
在c++11之前,对类中的成员变量的初始化要么是放在构造函数中,要么是在构造函数的初始化列表中:
class t1 {
public:
t1(int a) : name("kyo") {
age = a;
}
std::string name;
int age;
};
c++11则支持直接初始化:
class tt {
public:
int num = 6;
std::string remarks {"hello"};
t1 t = 6;
};
5. 列表初始化
在c++11之前的列表初始化运用如下:
struct st {
int a;
std::string str;
};
st s1 = {'a', "hello"};
上面是自定义类型的列表初始化,c++11对于列表初始化还支持于内置类型:
struct st {
int a;
std::string str;
};
st s1 {'a', "hello"};
int a {6};
char arr[] {1, 2, 3, 3, 5, 6};
运用于数组时显得十分方便。
6. 基于范围的for循环
int arr[] {2, 4, 6, 8};
int cnt = sizeof(arr) / sizeof(arr[0]);
在c++之前的for循环遍历数组:
for (int i = 0; i < cnt; ++i) {
int tmp = arr[i];
std:: cout << tmp << " ";
}
std::cout << std::endl;
在c++11等等效写法为:
for (int tmp : arr) {
std:: cout << tmp << " ";
}
std::cout << std::endl;
运用于引用同理:
for (int i = 0; i < cnt; ++i) {
int &tmp = arr[i];
tmp++;
std:: cout << tmp << " ";
}
std::cout << std::endl;
for (int &tmp : arr) {
tmp++;
std:: cout << tmp << " ";
}
std::cout << std::endl;
需要注意的是,上面c++11中的语法只能运用于数组大小可知的场合。
7. 静态断言
assert()是c/c++用的断言函数,如下代码:
#include <assert.h>
int main(void)
{
bool val = true;
assert(val == false);
printf("running...\n");
return 0;
}
assert(val == false)代码语句表示,程序运行时若条件为真则继续往下执行,若条件为假则中断程序并提示错误:
c++11提供了静态断言函数,在程序编译阶段就可以中断程序。需要强调的是,因为需要在编译阶段就需要作出断言结果,断言的条件只能是常量表达式。如下用于判断系统是多少位:
int main(void)
{
static_assert(sizeof(void* ) == 4, "local system is 64bit\n");
printf("running...\n");
return 0;
}
在64位的操作系统上编译:
8. noexcept关键字
noexcept用于声明函数不会抛出异常(君子协议):
int func2() noexcept
{
return 0;
}
跟下面的语义是一致的:
int func2() throw()
{
throw 1;
return 0;
}
c++11的noexcept在编译阶段会做出优化。
9. 强类型枚举
int main(void)
{
enum status0 {
success,
failed
};
enum status1 {
ok,
failed
};
return 0;
}
编译:
报错,这是因为枚举类型在同一个作用域中不能出现两次,即重复定义了。c++11中支持用class或struct关键字对enum作用域加以划分,且在使用该enum类型时需要指定作用域,称为强类型枚举:
enum class status0 {
success,
failed
};
enum class status1 {
ok,
failed
};
status0 val = status0::success; //需要指定作用域
强类型枚举还支持指定成员变量的类型:
enum class status0 {
success,
failed
};
enum class status1:char {
ok,
failed
};
status0 val = status0::success;
printf("%d\n", sizeof(status0::failed)); //默认是4字节,跟c语言的枚举类型一样默认是int类型
printf("%d\n", sizeof(status1::ok)); //1字节
10. nullptr
int func(int a)
{
printf("func(int )\n");
return 0;
}
int func(int* p)
{
printf("func(int* )\n");
return 0;
}
int main(void)
{
func(0);
func(NULL);
return 0;
}
如上重载函数,调用者希望func(0)调用的是func(int)原型函数,func(NULL)则调用func(int* ),然而:
编译器报错,func(NULL)不能匹配到调用函数,这是因为NULL其实质是一个0地址值。为了明确NULL指针,c++11引入了nullptr:
func(0);
func(nullptr);
11. constexpr常量表达式
常量表达的计算结果是发生在编译阶段,而非运行阶段。
const int get_num()
{
return 6;
}
int main(void)
{
enum Num {d1 = get_num(), d2};
return 0;
}
我们知道enum成员变量必须是一个常量,然而通过get_num()函数获取得值显然是通过非常量表达式的方法获取的,即必须在运行时才能知道d1的值,所以报错:
关键字constexpr可用于修饰表达式为一常量表达式:
constexpr int get_num()
{
return 6;
}
这样就编译正常了。同时还可以这么玩:
constexpr int get_num()
{
return 6;
}
int main(void)
{
constexpr int n = get_num();
enum Num {d1 = n, d2};
return 0;
}
需要强调,constexpr声明函数的限制:
(1)除了函数中只能有一个语句且该语句h是return语句;
(2)函数必须有返回值且,且只能返回常量/常量表达式,不可以是全局数据。
(3)在使用前必须先定义,注意是定义,单单声明不行;
推荐阅读