【c/c++】C++11中的新特性

说明

1. 新增关键词nullptr。

新增的nullptr是用来替代0或者NULL,来表示指向空(no value)的指针。

下面是一个例子:

void func(int i) {
	cout << "func(int i)" << endl;
}

void func(void *p) {
	cout << "func(void *p)" << endl;
}

int main() {
	int i = NULL;
	int j = 0;
	std::nullptr_t p = nullptr;
	func(i);
	func(j);
	func(p);
}

运行的结果:

func(int i)
func(int i)
func(void *p)

nullptr的类型是std::nullptr_t,定义于<cstddef>。

2. 增加auto来判断变量或对象类型。

auto可以用在各种类型上,包括lambda上也可以用。

但是auto必须要初始化操作,因为它需要初始化值来推导。

3. 使用{}引入“一致性初始化",或者也称为“列表初始化”。

列表初始化例子:

int main() {
	int i{};
	cout << "i = " << i << endl;//会有初始化,打印0
	int j = {};
	cout << "j = " << j << endl;//会有初始化,打印0
	//int k = { 2.0 };	//报错,因为有narrowing
	vector<int> v1 = { 1, 2, 3 };
	vector<int> v2{ 4, 5, 6 };
	for (auto a = v1.begin(); a != v1.end(); a++) {
		cout << *a << endl;
	}
	for (auto a = v2.begin(); a != v2.end(); a++) {
		cout << *a << endl;
	}
}

以上是基本类型的列表初始化(vector应该不算)。

但是有一个问题,i{}和j = {}是不是有什么差别?

对于基本类型,应该没有差别吧,因为本来也不涉及到构造函数。对于类的话,应该有通用的指明初始值列的构造函数。这需要使用到class template std::initializer_list<>。

下面是类的列表初始化示例:

class CLS {
private:
	int age;
	int height;
public:
	CLS(int a, int h) : age(a), height(h){
		cout << "CLS(int a, int h)" << endl;
	}
	CLS(std::initializer_list<int> vals) {
		cout << "CLS(std::initializer_list<int> vals)" << endl;
		auto a = vals.begin();
		age = *a;
		++a;
		height = *a;
	}
	void print(void) {
		cout << "age : " << age << endl;
		cout << "height : " << height << endl;
	}
};

int main() {
	CLS c1(1, 2);
	c1.print();
	CLS c2{ 3, 4 };
	c2.print();
	CLS c3 = { 5, 6 };	//调用的构造函数同c2{3, 4}
	c3.print();
	return 0;
}

运行的结果:

CLS(int a, int h)
age : 1
height : 2
CLS(std::initializer_list<int> vals)
age : 3
height : 4
CLS(std::initializer_list<int> vals)
age : 5
height : 6

4. c++11引入一种崭新的for循环形式,其实就是foreach循环。

下面是例子:

int main() {
	for (int a : {1, 2, 3, 4}) {
		cout << a << endl;
	}
	vector<int> v{ 5, 6, 7, 8 };
	for (auto& a : v) {//使用引用,就可以修改至,否则就不能修改;
		a *= 2;
	}
	for (auto a : v) {
		cout << a << endl;
	}
}

5. std::move,右值引用。

这个比较麻烦,这里不细讲了。

6. 关键字noexcept。

noexcept指明了函数不会抛出异常,或者它可能也或抛出异常,但是我们也处理不了,就不用管异常处理的事情了。

需要说明编译的时候是不会去管noexcept函数里面有没有抛出异常的,只是在运行时,如果一个noexcept函数抛出了异常,程序就会调用ternimate来终止程序本身。

另外noexcept也是一个运算符,后面接一个bool的参数:

void foo(void) noexcept;
void foo(void) noexcept(true);

上面两个函数是等价的,不过在vs中好像构成了重载,就是说两个都可以存在,编译能够通过。

7. 关键字constexpr。

constexpt关键字让表达式在编译器就是常量,因此可以用在一些只能是常量的地方。比如下面的例子:

constexpr int square(int x) {
	return x * x;
}

int main() {
	int a[square(3)]{};
	return 0;
}

下面的例子中就移动要用constexpr来声明square,否在main()中的a数组定义时会报错。

8. lambda表达式。

它的格式有两种:

[...] {...}

还有更复杂一些的:

[...] (...) mutable throw语句 ->retType {...}

多出来的部分都是可选的。

[]部分称为capture,其实就是从包含该lambda表达式的函数中的参数,它们传到lambda表达式的方式两种,一种是传值,一种是传引用。用=来表示传值,或者不写也表示传值;用&来表示传引用,传引用的时候要注意的是这个值会在不同的阶段改变,随之而来的是lambda表达式的这个变量也变了。

下面是几个例子:

int main() {
	auto l = [] {
		return 42;//不用指定类型,那么编译器就自动推断,比如这里就是int
	};//分号不能忘
	cout << l() << endl;//l后面的()不能忘,因为它是函数对象,打印42
	
	/*l = []() -> double {
		return 42;
	};*/ //两个lambda类型不一样,所以不能这么用
	auto ll = []() -> double {//指定了返回类型,这个时候就一定要把()也写上,不然会报错
		return 42;
	};
	cout << ll() << endl;//打印42
	int x = 0;
	int y = 0;
	auto lll = [x, &y]() {
		cout << "x: " << x << " " << "y: " << y << endl;
		++y;
		//++x;这么写直接报错
	};
	x = y = 1;
	lll();//打印x: 0 y: 1,注意前面的y=1有生效。
	lll();//打印x: 0 y: 2

	//使用mutable可以让传值变量也可以改变:
	int z = 0;
	auto llll = [z]() mutable {
		++z;
		cout << "z: " << z << endl;
	};
	z = 100;//但是无论是不是mutable,这个赋值对于llll都没有用
	llll();//打印z: 1
}

9. 关键字decltype。

这个关键字让编译器能够得到表达式的类型。例如:

int main() {
	std::map<std::string, float> coll;
	decltype(coll)::value_type elem;
}

10. c++11新增的类型:char16_t / char32_t / long long / unsigned long long / std::nullptr_t。

11. c++会在main()中定义一个隐式的return 0。

所有main中不写return也可以。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值