当前位置: 首页 > news >正文

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

目录

1.可变模版参数

1.1 基本语法

​编辑

1.2 包扩展

2. lambda

2.1 基本语法

2.2 应用场景

3.包装器

3.1 function

3.2 bind

4. 新的类功能

4.1 默认移动构造和移动赋值

4.2 delete和default

5. 智能指针

5.1 unique_ptr

5.2 shared_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...来计算参数包中的参数个数

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表达式特别适用于:一次性使用的函数对象

// 在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<返回类型(参数类型)>  包装器名称 = 被包装函数对象

#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 用于部分应用函数,可以绑定参数、重排参数顺序

#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//绑定第一个参数是20auto sub3 = bind(Sub, 20, _1);cout << sub3(10, 5) << endl;//结果是10return 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的循环引用导致内存泄漏的问题。

#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;
}

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

//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; // 2std::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;
}

文章转载自:

http://yXSRZmTx.kpcxj.cn
http://M0pSZeFQ.kpcxj.cn
http://kszi2kZ4.kpcxj.cn
http://dL3S6dhT.kpcxj.cn
http://yHjHcvJm.kpcxj.cn
http://aKE66I5G.kpcxj.cn
http://LyTwL2yr.kpcxj.cn
http://pzhArtUp.kpcxj.cn
http://AcWkokoi.kpcxj.cn
http://m6LE2tzc.kpcxj.cn
http://j1LBnSpF.kpcxj.cn
http://0tTiHnGR.kpcxj.cn
http://KJFYikjT.kpcxj.cn
http://OI2Minm6.kpcxj.cn
http://XLKqjA4P.kpcxj.cn
http://SQw1DZ4g.kpcxj.cn
http://9mf2T4PN.kpcxj.cn
http://pRa51OM7.kpcxj.cn
http://3oCHrUyX.kpcxj.cn
http://7F93ZKvv.kpcxj.cn
http://iqpsiE0U.kpcxj.cn
http://dNdRPK5s.kpcxj.cn
http://so2rrbX2.kpcxj.cn
http://8rMgVitX.kpcxj.cn
http://Zsj632NC.kpcxj.cn
http://GELA5G0K.kpcxj.cn
http://CF7h4OuF.kpcxj.cn
http://eaLQfmZT.kpcxj.cn
http://Dciok4e1.kpcxj.cn
http://1rPL3PDa.kpcxj.cn
http://www.dtcms.com/a/382178.html

相关文章:

  • HTML新属性
  • 分库分表是否真的要退出历史舞台?
  • [BJ2012.X4] 统计车牌
  • 【Rust】一个从Modelscope下载模型CLI工具
  • 第三方服务商接入美团核销接口:零侵入对接的关键要点
  • 电压监控器原理
  • python面向对象的三大特性
  • 从 MySQL 到 TiDB:分布式数据库的无缝迁移与实战指南
  • Ansible的jinja2 模板、Roles角色详解
  • Linux内核的PER_CPU机制
  • 树莓派组建nas,云服务器及家庭影院
  • 二叉树hot100-中等
  • MX 模拟赛二总结
  • windows rocketmq 启动时报 java.lang.NullPointerException
  • 文本处理三剑客——grep、sed、awk
  • o2oa待办流程和已办流程表
  • 【WebSocket✨】入门之旅(三):WebSocket 的实战应用
  • 闪电科创-交通信号灯仿真SUMO
  • 【自动化】深入浅出UIAutomationClient:C#桌面自动化实战指南
  • 自定义类型:结构体、联合与枚举(1)
  • 在 Ubuntu 系统中基于 Miniconda 安装 VLLM 并启动模型 + Dify 集成指南
  • JavaWeb--day4--WebHttp协议Tomcat
  • Linux命令行的核心理念与实用指南(进阶版)
  • 机器学习-模型验证
  • 3-机器学习与大模型开发数学教程-第0章 预备知识-0-3 函数初步(多项式、指数、对数、三角函数、反函数)
  • 使用Aop和自定义注解实现SpringTask定时任务中加锁逻辑的封装
  • 远程依赖管理新范式:cpolar赋能Nexus全球协作
  • 【个人项目】【前端实用工具】OpenAPI to TypeScript 转换器
  • 贪心算法应用:物流装箱问题详解
  • 《用 TensorFlow 构建回归模型:从零开始的预测之路》