【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<int> 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 <functional>

std::function<int(int, int)> adder = [](int a, int b) { return a + b; };
int result = adder(2, 3); // 结果为5

3.2 bind

std::bind 用于部分应用函数,可以绑定参数、重排参数顺序

cpp 复制代码
#include<iostream>
#include<functional>
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<iostream>
#include<memory>

using namespace std;

int main()
{
	auto_ptr<int> ap1(new int(3));
	auto_ptr<int> ap2(ap1);//此时ap1悬空
	unique_ptr<int> up1(new int(5));
	unique_ptr<int> up2(move(up1));//可以移动,但此时up1已经悬空,要注意
	//unique_ptr<int> up3(up1);//不支持拷贝,会报错
	shared_ptr<int> sp1(new int(10));
	shared_ptr<int> sp2(move(sp1));//支持移动,但sp1也悬空了
	shared_ptr<int> sp3(sp2);//也支持拷贝
	return 0;
}

这里我们可以看到被移动后的指针都悬空了,所以在移动时一定要小心!

cpp 复制代码
//weak_ptr使用场景
#include <memory>
#include <iostream>

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::shared_ptr<A> a_ptr; // 这里会造成循环引用!
    ~B() { std::cout << "B destroyed\n"; }
};

class C {
public:
    std::weak_ptr<A> a_ptr; // 使用 weak_ptr 打破循环引用
    ~C() { std::cout << "C destroyed\n"; }
};

void testCycle() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();

    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<A>();
    auto c = std::make_shared<C>();

    c->a_ptr = a; // weak_ptr 不增加引用计数

    std::cout << "A use count: " << a.use_count() << std::endl; // 1
} // 正常释放
int main()
{
    testCycle();
    testNoCycle();
	return 0;
}
相关推荐
洛水水4 分钟前
【力扣100题】82.有效的括号
c++·算法·leetcode
云水-禅心6 分钟前
解决MacOS 安装Python之后默认版本指向不正确问题
开发语言·python·macos
冰暮流星8 分钟前
javascript之this关键字
开发语言·前端·javascript
rit84324998 分钟前
基于Qt的串口上位机控制蓝牙小车程序
开发语言·qt
百度Geek说9 分钟前
CodingAgent 的原始森林困境:一张地图能解决什么?
开发语言·javascript·ecmascript·coding agent
sunny.day17 分钟前
js原型与原型链
开发语言·javascript·原型模式·js原型链
weixin_5231853219 分钟前
Java内存模型详解:栈、堆、方法区、本地方法栈与程序计数器
java·开发语言
换个昵称都难32 分钟前
WebRTC QoS 实战:从原理到弱网优化
开发语言·php·webrtc
初中就开始混世的大魔王33 分钟前
7 Fast DDS-持久化服务
c++·人工智能·中间件·自动驾驶·信息与通信
爱吃生蚝的于勒41 分钟前
QT开发第三章——常用控件
linux·服务器·开发语言·前端·javascript·c++·qt