nullptr
不能对nullptr取地址。
int test2() {
int * p = nullptr;
cout << &nullptr; //此处会编译报错,因为nullptr是一个右值常量,不能被取地址。
}
列表初始化
可以使用以下几种形式完成列表初始化:
- 等号加上赋值表达式,int a = 3+4;
- 等号加上花括号的初始化列表,int a = {3 + 4};
- 圆括号样式的表达式列表,int a(3,4);
- 花括号式的初始化列表,int a{3,4};
后两者也可以用于new的初始化,例如:
- int * i = new int(1);
- double * d = new double{1.2f};
列表初始化可以在{}之前使用=,其效果与不使用=的方式相同。
例子1:
#include "vector"
#include "map"
using namespace std;
int test9() {
int a[] = {1,2,3};
int b[]{2,4,6}; //初始化列表前是否使用=无区别。
vector<int> c{1,3,5};
map<int, float> d = {{1,1.1},{2,2.2},{3, 3.3},{4, 4.4}};
map<int, float> e {{1, 1.1},{2, 2.2},{3, 3.3}}; //初始化列表前带不带=效果相同。
return 0;
}
自定义类型的初始化函数可以使用初始化列表,
#include "string"
using namespace std;
int test10() {
class People {
public:
//自定义类型的构造函数中可以使用初始化列表。
People(initializer_list<string> l) {
auto i = l.begin();
for(; i != l.end(); i++) {
data.push_back(*i);
}
}
void show() {
auto i = data.begin();
for(; i != data.end(); i++) {
cout << *i << endl; //vector的迭代器通过此种方式获取元素。
}
}
private:
vector<string> data;
};
//People p{{'a', 'b', 'c'}}; //会打印abc
People p{{"a", "b", "c"}}; //此时会打印a \n b \n c \n 带有换行。
People p1 = {{"a1","a2","a3"}};
//由上述两行可见,成员初始化列表前带或者不带等号都可以。
p.show();
p1.show();
return 0;
}
函数的参数列表也可以使用成员初始化列表,例如:
void test11(initializer_list<int> iv) {
auto i = iv.begin();
for(; i != iv.end(); i++) {
cout << *i << " ";
}
cout << endl;
}
void test12() {
test11({1,2,3}); //使用成员初始化列表进行初始化
test11({}); //空列表
}
类的成员函数也可以使用初始化列表,包括一些操作符的重载函数。
初始化列表还能够用于函数的返回值,例如:
#include "deque"
deque<int> test12() {
test11({1,2,3}); //使用成员初始化列表进行初始化
test11({}); //空列表
return {2,3,4}; //返回初始化列表。
}
类型收窄
类型收窄一般是指一些可以使得数据变化或者精度丢失的隐式类型转换。
- 在C++中,列表初始化是唯一一种可以防止类型收窄的初始化方式。
列表初始化改变了C++中类型初始化的基本模式,有效的统一了基本类型和自定义类型的行为。这也是C++11设计的所遵循的一个思想,即通用为本,专用为末。
DECLTYPE
对于动态类型的支持
- C完全不支持动态类型。
- C++部分支持动态类型,C++对动态类型的支持就是C++中的运行时类型识别(RTTI)。
可以通过typeid查询一个变量的类型,通过typeid查询类型的话会返回一个type_info类型的数据。在C++11中,又增加了hash_code这个成员函数,返回对应类型的唯一的Hash值,使用这个hash值可以方便的比较两个类型是否相同。
#include "vector"
#include "typeinfo"
int test13() {
int a;
vector<string> vec;
class Write{};
cout << typeid(a).name() << " " << typeid(a).hash_code() << endl;
cout << typeid(vec).name() //显示类型名称
<< " "
<< typeid(a).hash_code() //显示类型对应的hash值,hash_code是运行时才能得到信息的。
<< endl;
cout << typeid(Write).name() << endl;
return 0;
}
- auto和decltype都是在编译的时候就进行类型推导的。
- 与auto类型推导时不能带走cv限制符不同,decltype能够带走表达式的cv限制符。
is_const和is_volatile
void test14() {
const int a = 1;
volatile int b = 2;
int c = 2;
cout << is_const<decltype(a)>::value << endl;
//判断a是否时常量。
cout << is_volatile<decltype(a)>::value << endl;
//判断a是否是易失的。
cout << is_const<decltype(b)>::value << endl;
cout << is_volatile<decltype(b)>::value << endl;
cout << is_const<decltype(c)>::value << endl;
cout << is_volatile<decltype(b)>::value << endl;
}
基于范围的for循环
遍历数组的三种方式:
- 通过数组长度来遍历数组,例子
- 通过标准模板库中的方法来遍历数组,例子
- 通过C++11的新特性“基于范围的for循环”来遍历数组,例子
#include "algorithm"
void test15() {
int a[] = {1,2,3,4,5};
vector<int> vec_a {11,22,33,44,55};
//方法一,通过数组长度遍历数组。
for(int i = 0; i != sizeof(a)/sizeof(a[0]); i++) {
cout << a[i] << " ";
}
cout << endl;
//方法二,通过模板库中的方法遍历数组。
auto lambdaFunc = [](auto a) {cout << a << " ";}; //可以使用auto声明lambda函数的形参。
//但是如果使用auto声明普通函数的形参则是不允许的。
for_each(a, a + sizeof(a)/sizeof(a[0]), lambdaFunc);
cout << endl;
for_each(vec_a.begin(), vec_a.end() , lambdaFunc);
cout << endl;
//方法三,通过范围for
for(auto item : a) { //如果需要修改数组内容,则要声明成auto & item,否则声明成auto item。
cout << item << " ";
}
cout << endl;
}
- 基于范围的for循环中迭代的变量采用了引用的形式,如果迭代变量的值在循环中不会被修改,那么我们完全可以不用引用的方式来迭代变量。即for(int & a : arr);和for(int a : arr);的区别。
如果我们的数组的大小不能够被确定,则不能使用基于范围的for循环。例如:一个数组作为函数参数的时候,其实数组是被作为指针传递进去的,此时在函数内部并不知道传递进来的指针代表的数组的长度,因此,此种情况,如果在函数内部调用范围for处理数组就会报错。
原子类型与原子操作
所谓原子类型,就是多线程程序中“最小的且不可并行化的”操作。
P198