【C++】C++11介绍(Ⅱ)

目录

1.可变模版参数

[1.1 基本语法](#1.1 基本语法)

​编辑

[1.2 包扩展](#1.2 包扩展)

[2. lambda](#2. lambda)

[2.1 基本语法](#2.1 基本语法)

[2.2 应用场景](#2.2 应用场景)

3.包装器

[3.1 function](#3.1 function)

[3.2 bind](#3.2 bind)

[4. 新的类功能](#4. 新的类功能)

[4.1 默认移动构造和移动赋值](#4.1 默认移动构造和移动赋值)

[4.2 delete和default](#4.2 delete和default)

[5. 智能指针](#5. 智能指针)

[5.1 unique_ptr](#5.1 unique_ptr)

[5.2 shared_ptr](#5.2 shared_ptr)

[5.3 weak_ptr](#5.3 weak_ptr)


1.可变模版参数

1.1 基本语法

可变模板参数是C++11引入的强大特性,允许模板接受任意数量和类型的参数。其基本语法使用省略号(...)表示参数包

template<class ...Args> void Func(Args... args) {}

template<class ...Args> void Func(Args&... args) {}

template<class ...Args> void Func(Args&&... args) {}

这里的Args是模板参数包,args是函数参数包,可以包含零个或多个参数。

我们可以用sizeof...来计算参数包中的参数个数

cpp 复制代码
template <class ...Args>
void Print(Args&&... args)
{
	cout << sizeof...(args) << endl;
}

int main()
{
	double x = 2.2;
	Print(); // 0个参数 
	Print(1); // 1个参数 
	Print(1, string("aaaaa")); // 2个参数 
	Print(1.1, string("bbbbbb"), x); //3个参数 
	return 0;
}

1.2 包扩展

参数包本身不能直接使用,需要通过包扩展来展开。常见的扩展方法就是递归推导

2. lambda

Lambda表达式是C++11引入的一种定义匿名函数对象的简洁方式。它允许我们在需要函数的地方直接内联定义函数,而无需单独声明命名函数。

2.1 基本语法

捕获变量 \](参数)-\> 返回类型 {函数体}

捕获列表有以下几种形式:

\] 不捕获任何变量 \[=\] 以值方式捕获所有外部变量 \[\&\] 以引用方式捕获所有外部变量 \[a, \&b\] 混合捕获方式 #### 2.2 应用场景 Lambda表达式特别适用于:一次性使用的函数对象 ```cpp // 在STL算法中使用lambda std::vector nums = {1, 2, 3, 4, 5}; std::for_each(nums.begin(), nums.end(), [](int n) { std::cout << n * 2 << " "; }); ``` ### 3.包装器 #### 3.1 function std::function 是一个通用的函数包装器,可以存储、复制和调用任何可调用对象 > function\<返回类型(参数类型)\> 包装器名称 = 被包装函数对象 ```cpp #include std::function adder = [](int a, int b) { return a + b; }; int result = adder(2, 3); // 结果为5 ``` #### 3.2 bind std::bind 用于部分应用函数,可以绑定参数、重排参数顺序 ```cpp #include #include using namespace std; using placeholders::_1; using placeholders::_2; using placeholders::_3; int Sub(int a, int b) { return a-b; } int main() { // bind 本质返回的是一个仿函数对象 // 调整参数顺序 // _1代表第一个实参 // _2代表第二个实参 // ... auto sub1 = bind(Sub, _1, _2); cout << sub1(10, 5) << endl;//结果是5 //调整参数顺序 auto sub2 = bind(Sub, _2, _1); cout << sub2(10, 5) << endl;//结果是-5 //绑定第一个参数是20 auto sub3 = bind(Sub, 20, _1); cout << sub3(10, 5) << endl;//结果是10 return 0; } ``` ### 4. 新的类功能 #### 4.1 默认移动构造和移动赋值 前面我们已经学过了C++类中,有6个默认成员函数:构造函数/析构函数/拷贝构造函数/拷贝赋值重载/取地址重载/const取地址重载。我们用的最多的是前四个,这些函数都是我们在类中不写编译器会自动生成的。C++11新增了两个默认成员函数,就是移动构造函数和移动赋值重载。 如果你没有实现移动构造,且没有实现析构、拷贝构造、拷贝赋值中的任意一个。那么编译器会自动生成一个默认移动构造; 如果你没有实现移动赋值重载函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。 默认生成的移动构造/移动赋值重载,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。 #### 4.2 delete和default 函数声明 = default:显式要求编译器生成默认版本的函数 函数声明 = delete:禁止使用某些函数 ### 5. 智能指针 C++98设计的一个智能指针叫 auto_ptr ,它的特点是在拷贝时把被拷贝对象的资源管理权交给拷贝对象,但这样可能会使被拷贝对象悬空,从而报错。这是一个不太好的智能指针,不建议使用。 C++11之后引入了三种智能指针,用于自动管理动态分配的内存: #### 5.1 unique_ptr unique_ptr 正如它的名字所说,它的特点就是具有唯一性,不支持拷贝,只支持移动。 #### 5.2 shared_ptr shared_ptr 的特点是支持拷贝,也支持移动,共享资源的管理,使用引用计数方式实现 #### 5.3 weak_ptr weak_ptr 不支持RAII,也就是说它不能直接管理资源,其产生的本质是解决shared_ptr的循环引用导致内存泄漏的问题。 ```cpp #include #include using namespace std; int main() { auto_ptr ap1(new int(3)); auto_ptr ap2(ap1);//此时ap1悬空 unique_ptr up1(new int(5)); unique_ptr up2(move(up1));//可以移动,但此时up1已经悬空,要注意 //unique_ptr up3(up1);//不支持拷贝,会报错 shared_ptr sp1(new int(10)); shared_ptr sp2(move(sp1));//支持移动,但sp1也悬空了 shared_ptr sp3(sp2);//也支持拷贝 return 0; } ``` ![](https://i-blog.csdnimg.cn/direct/530cb0f0a6e94359afad591656ce249f.png) 这里我们可以看到被移动后的指针都悬空了,所以在移动时一定要小心! ```cpp //weak_ptr使用场景 #include #include class B; // 前向声明 class A { public: std::shared_ptr b_ptr; ~A() { std::cout << "A destroyed\n"; } }; class B { public: std::shared_ptr a_ptr; // 这里会造成循环引用! ~B() { std::cout << "B destroyed\n"; } }; class C { public: std::weak_ptr a_ptr; // 使用 weak_ptr 打破循环引用 ~C() { std::cout << "C destroyed\n"; } }; void testCycle() { auto a = std::make_shared(); auto b = std::make_shared(); a->b_ptr = b; b->a_ptr = a; // 循环引用!内存泄漏! std::cout << "A use count: " << a.use_count() << std::endl; // 2 std::cout << "B use count: " << b.use_count() << std::endl; // 2 } // a 和 b 的引用计数永远不为0,无法释放! void testNoCycle() { auto a = std::make_shared(); auto c = std::make_shared(); c->a_ptr = a; // weak_ptr 不增加引用计数 std::cout << "A use count: " << a.use_count() << std::endl; // 1 } // 正常释放 int main() { testCycle(); testNoCycle(); return 0; } ```

相关推荐
你怎么知道我是队长2 小时前
C语言---存储类
c语言·开发语言
XIAOYU6720132 小时前
金融数学专业需要学哪些数学和编程内容?
开发语言·matlab·金融
油炸自行车2 小时前
【Qt】编写Qt自定义Ui控件步骤
开发语言·c++·qt·ui·自定义ui控件·qt4 自定义ui控件
浪扼飞舟3 小时前
c#基础二(类和对象,构造器调用顺序、访问级别、重写和多态、抽象类和接口)
java·开发语言·c#
yuanpan3 小时前
python标准库有哪些模块,简单总结下。
开发语言·python
听情歌落俗3 小时前
MATLAB3-2数据存储-台大郭彦甫
开发语言·数学建模·matlab·矩阵
云天徽上4 小时前
【数据可视化-112】使用PyEcharts绘制TreeMap(矩形树图)完全指南及电商销售数据TreeMap绘制实战
开发语言·python·信息可视化·数据分析·pyecharts
呱呱巨基4 小时前
C/C++ 内存管理
c++·笔记·学习
半桔4 小时前
【网络编程】TCP 服务器并发编程:多进程、线程池与守护进程实践
linux·服务器·网络·c++·tcp/ip