C++11新特性

29 篇文章 1 订阅

1. 列表初始化

1.1 {} 初始化

c++11引入了一切皆可{}初始化,例如:

class Test1
{
public:
	Test1(int d1, int d2)
		:data1_(d1), data2_(d2)
	{
		cout << "Test1(int d1, int d2)" << endl;
	}
private:
	int data1_;
	int data2_;
};

int main()
{
	int a = 1;
	int b = { 1 };
	int c{ 1 };
	cout << "a " << a << endl;
	cout << "b " << b << endl;
	cout << "c " << c << endl;

    //本质:调用构造函数
	Test1 t1(2, 2);
	Test1 t2 = { 2,2 };	//多参数隐式类型转换
	Test1 t3{ 2,2 };
    
	//类型转换生成临时对象,临时对象具有常性,所以要加const
    const Test1 &rt = { 4, 4 };
    
	int* ptr1 = new int[3] {1, 2, 3};
	
	return 0;
}
//输出:
//a 1
//b 1
//c 1
//Test1(int d1, int d2)
//Test1(int d1, int d2)
//Test1(int d1, int d2)

1.2 initializer_list

class Test1
{
public:
	Test1(int d1, int d2)
		:data1_(d1), data2_(d2)
	{
		cout << "Test1(int d1, int d2)" << endl;
	}
private:
	int data1_;
	int data2_;
};

int main()
{
	vector<int> v = { 1,2,3,4,5 };
	Test1 t1 = { 1,2 };

	auto il = { 1,2,3,4,5,6 };
	cout << typeid(t1).name() << endl;
	cout << typeid(il).name() << endl;
	cout << typeid(v).name() << endl;
	return 0;
}
//输出:
//Test1(int d1, int d2)
//class Test1
//class std::initializer_list<int>
//class std::vector<int,class std::allocator<int> >

这里的三种{}初始化,vector初始化时{}内参数可以控制多少,而我们Test1这个类,就只能带2个参数,以为构造只有2个参数。

这里是因为vector构造重载了initializer_list

image-20240309194950191

initializer_listC++11新增的(可以理解成容器),它底层的实现逻辑:

//{1, 2, 3, 4, 5} 常量数组 存在常量区
template<class T>
class initializer_list
{
    const T *start;		//指向常量地址的起始
    const T *finish;	//结束位置下一个
}

image-20240309195952388

所以将{1, 2, 3}数组识别成了initializer_list,本质上调用的是它的构造函数。

2. 声明

2.1 auto

这算是C++中的一个语法糖了,自动推导类型,初始化日常会经常用到

auto要求必须显示初始化

2.2 decltype

decltype也是一个推导类型,它可以用来定义变量,不初始化

比较:

  • auto:自动推导类型,必须初始化——只能用
  • typeid(type).name():推导类型,是一个字符串,无法使用——只能看
  • decltype:自动推导类型,可以不初始化——能看能用
void Print(size_t sz)
{
	cout << "分配内存:" << sz << endl;
}

template<class Func>
class Test2
{
public:
	Test2(Func f)
		:f_(f)
	{}
public:
	void callFunc(size_t sz)
	{
		f_(sz);
	}
private:
	Func f_;
};


int main()
{
	auto pf = malloc;	//函数指针,指向了malloc
	int* a = (int*)pf(sizeof(int));
	Test2<decltype (pf)> t2(pf);
	t2.callFunc(sizeof(int));

	Test2<decltype(&Print)> tp(&Print);
	tp.callFunc(sizeof(long long));
	return 0;
}

2.3 nullptr

C++中将NULL定义成了0,所以引入nullptr来表示空指针

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif
void Func1(int x)
{
	cout << "Func1(int x)" << endl;
}

void Func1(int *p)
{
	cout << "Func1(int *p)" << endl;
}
int main()
{
	Func1(NULL);
	Func1(nullptr);
	return 0;
}
//输出:
//Func1(int x)
//Func1(int *p)

3. 范围for

这个也算是C++的语法糖,日常经常用,底层是迭代器

对原生数组的支持是编译器的特殊支持,处理成了指针

4. STL的变化

4.1 新容器

image-20240309205123758

这些都是C++11新增的容器unordered_mapunordered_set底层是哈希表。

之前有写过模拟实现:基于哈希表对unordered_map和unordered_set的封装

array算是比较鸡肋的容器,它是一个静态数组~,唯一好处对于越界的检查,比原生数组检查严格,底层调用了operator[]

array文档

forward_list这也一点鸡肋~,它是一个单链表,只支持了头插头删,它的insert是在当前位置之后插入,因为单链表的尾插尾删和前面插入,都需要找前一个位置,代价较大

forward_list文档

4.2 新接口

  1. 新增了一个const版本的迭代器…

    image-20240309210418686

  2. 所有容器支持了{}列表初始化的构造函数

    image-20240309210850835

  3. 所有容器新增emplace系列,即右值引用和模板可变参数 -> 大幅性能提升

    image-20240309211117887

5. 右值引用

单独写了一篇博客:C++11新特性【右值引用】

6. 可变参数模板

在学C语言的时候,例如printfscanf都是可变参数

image-20240313124056833

函数可变参数传的是对象,而C++11新增的可变参数模板,传的是类型

//一个一个取出参数
template<class T>
void PrintList(T val)
{
	cout << val << " " << endl;
}

template<class T, class ...Args>
void PrintList(T val, Args...args)
{
	//输出参数个数
	//cout << sizeof...(args) << endl;
	
    cout << val << " ";
	PrintList(args...);	//递归式调用推参数包的值
}
int main()
{
	PrintList(1);
	PrintList(1, 2.5);
	PrintList(1, 2.5, "hello");
}
//一次性全部取出
class Date
{
public:
	Date(int year = 2001, int month = 6, int day = 8)
		:year_(year), month_(month), day_(day)
	{}
	~Date()
	{}
private:
	int year_;
	int month_;
	int day_;

};

template<class ...Args>
Date* Create(Args...args)
{
	Date* ret = new Date(args...);
	return ret;
}
int main()
{
	Date* d = Create();
	Date* d1 = Create(2024);
	Date* d2 = Create(2024, 3);
	Date* d3 = Create(2024, 3, 14);
	Date* d4 = Create(d3);
	return 0;
}

C++11新增的emplace系列就是可变参数模板

image-20240313132831306

不用先去构造对象在拷贝构造,可以直接构造了

7. lambda表达式

单独写了一篇博客:C++11——lambda表达式

8. 包装器

8.1 function

C++的调用类型太多,例如函数指针、函数对象、lambda、仿函数表达式等:

template<class F,class T>
T func(F f, T t)
{
	static int count = 0;
	cout << "count: " << count << endl;
	cout << "count: " << &count << endl;
	return f(t);
}

int Mul(int data)
{
	return data * 2;
}

struct Muls
{
	int operator()(int data)
	{
		return data * 3;
	}
};

int main()
{	
	cout << func(Mul, 10) << endl;	//函数指针
	cout << func(Muls(),10) << endl;	//函数对象
	cout << func([](int data) {return data * 4; }, 10) << endl;	//lambda表达式
	return 0;
}
//输出:
//	count: 0
//	count: 00007FF7DB82D170
//	20
//	count: 0
//	count: 00007FF7DB82D174
//	30
//	count: 0
//	count: 00007FF7DB82D178
//	40

这里的函数模板被实例化成了三份,这里如果想要将这个调用对象存到容器当中,例如vector,这里就无法存进去,因为不晓得要实例化成那种,包装器就能解决这个问题:

#include<functional>	//需包头文件
template<class F,class T>
T func(F f, T t)
{
	static int count = 0;
	cout << "count: " << count << endl;
	cout << "count: " << &count << endl;
	return f(t);
}

int Mul(int data)
{
	return data * 2;
}

struct Muls
{
	int operator()(int data)
	{
		return data * 3;
	}
};

int main()
{
    //function<返回类型(参数类型)>
	function<int(int)> f1 = Mul;
	function<int(int)> f2 = Muls();
	function<int(int)> f3 = [](int data) {return data * 4; };
	vector<function<int(int)>> vf = { f1,f2,f3 };
	int n = 2;
	for (auto e : vf)
	{
		cout << e(n++) << endl;
	}
	return 0;
}

这里用来包装器之后,只实例化了一份:
image-20240313160721218

8.2 bind

bind是一个函数模板,可以调整调用参数的顺序

int Sub(int x, int y)
{
	return x - y;
}

double Rate(int x, int y, double rate)
{
	return (x + y) * rate;
}


class A
{
public:
	double Rate(int a, int b, double rate)
	{
		return (a + b) * rate;
	}
	static double sRate(int a, int b, double rate)
	{
		return (a + b) * rate;
	}
};
int main()
{
	//静态采用&	非静态有this指针
	function<double(int, int)> rate1 = bind(&A::Rate, A(), placeholders::_1, placeholders::_2, 1.5);
	function<double(int, int)> rate1 = bind(&A::sRate, placeholders::_1, placeholders::_2, 1.5);

	//function<double(int, int)> rate1 = bind(Rate, placeholders::_1, placeholders::_2, 1.5);
	//function<double(int, int)> rate2 = bind(Rate, placeholders::_1, placeholders::_2, 2.6);
	//function<double(int, int)> rate3 = bind(Rate, placeholders::_1, placeholders::_2, 3.7);
	//cout << rate1(9, 27) << endl;
	//cout << rate2(9, 27) << endl;
	//cout << rate3(9, 27) << endl;

	//function<int(int, int)> lsub = bind(Sub, placeholders::_1, placeholders::_2);
	//function<int(int, int)> rsub = bind(Sub, placeholders::_2, placeholders::_1);
	//cout << rsub(10, 5) << endl;
	//cout << lsub(10, 5) << endl;

	return 0;
}

底层都是仿函数

9. 新的类默认成员函数

在C++11之前,类只有6个默认成员函数:

  • 构造
  • 拷贝构造
  • 赋值重载
  • 析构
  • 取地址重载
  • const取地址重载

最常用的是前四个,C++11新增了2个:

  • 移动构造
  • 移动赋值运算符重载

默认构造函数,我们不写编译器会自动生成

  • 深拷贝的类型需要自己实现
  • 浅拷贝的类不需要自己实现

如果没有自己实现移动构造,且没有实现析构、拷贝、拷贝赋值运算符重载中的任意一个。那么编译器会自动生成默认移动构造。默认生成的会对内置类型成员逐字节拷贝,自定义类型成员,需要看这个成员是否需要移动构造,如果实现了调用移动构造,没实现就调用拷贝构造。

这里如果需要自己手动写析构、拷贝、赋值运算符重载,肯定是一个深拷贝的类,那肯定是需要自己去自己写移动构造和移动赋值的;如果这些都不用写,那直接用编译器默认生成的肯定更香一点。

10. 线程库

关系线程这块,有写个原生线程的内容,掌握了原生线程,对于封装线程的使用也就很简单了:

Linux线程概念

Linux多线程——线程控制

Linux多线程——互斥锁

Linux多线程——生产消费者模型

Linux多线程——线程池

官方文档:thread - C++ Reference (cplusplus.com)mutex - C++ Reference (cplusplus.com)

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加法器+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值