【C++11 新特性】从 “test01” 到 “看得懂”:菜鸡的复习笔记(附改名 + 注释版代码)
各位未来的自己:当你翻开这篇笔记时,大概率正对着当初写的test07()、func11()一脸懵 —— 咱当初为了省事儿,函数名起得跟 “密码” 似的,注释更是约等于没有。现在我把这些 “加密代码” 解密,改成 “人话” 函数名,加了比代码还长的注释,还顺便吐槽了当初踩过的坑,主打一个 “以后复习不骂街”!
一、先总览:咱的代码里藏了哪些 C++11 新特性?
先列个 “特性清单”,省得你翻半天不知道重点:
auto(自动类型推导)、
decltype(类型照妖镜)、
右值引用(&&)、
智能指针(shared_ptr/weak_ptr/unique_ptr)、
Lambda 表达式(匿名函数)、
可变参数模板(万能参数包)、
新增容器(array/forward_list/无序容器)、
范围 for 循环、
默认 / 删除函数(=default/=delete)。
下面逐个拆解,每个特性都配 “改名后代码 + 大白话注释 + 踩坑记录”。
二、逐个攻破:C++11 新特性详解(附改名代码)
1. auto:不用记类型的 “自动快递员”
核心作用:让编译器帮你推导出变量类型,不用手动写vector<int>::iterator这种长到哭的类型。当初的坑:分不清auto对const和引用的推导规则,瞎写导致编译报错。
改名后代码(原 test01→TestAutoSimplifyCode)
cpp
#include <vector>
#include <iostream>
using namespace std;
// 测试auto的核心用法:简化迭代器、处理const和引用
void TestAutoSimplifyCode() {
// 场景1:用auto简化vector迭代器(不用写vector<int>::iterator)
vector<int> num_vec = { 1,2,3,4,51 };
cout << "场景1:auto简化迭代器" << endl;
// 原写法:for (vector<int>::iterator it = num_vec.begin(); it != num_vec.end(); it++)
for (auto it = num_vec.begin(); it != num_vec.end(); it++) {
cout << *it << " "; // 输出:1 2 3 4 51
}
cout << endl << endl;
// 场景2:auto对const和引用的推导规则(重点!当初踩过坑)
cout << "场景2:auto与const、引用的爱恨情仇" << endl;
int x = 0;
const auto n = x; // auto推导为int,保留const→const int(n只读,不能改)
auto f = n; // auto推导为int,丢了const→int(f能改)
const auto& r1 = x; // auto推导为int,保留const和引用→const int&(r1只读)
auto& r2 = r1; // auto推导为const int,保留引用→const int&(r2也只读)
// 测试修改(注释掉的是编译报错的情况,当初踩过的坑!)
x = 100; // 可以改:x是普通int
// n = 100; // 报错:n是const int,只读
f = 100; // 可以改:f是普通int
// r1 = 100; // 报错:r1是const int&,只读
// r2 = 100; // 报错:r2是const int&,只读
cout << "x=" << x << ", n=" << n << ", f=" << f << endl; // 输出:x=100, n=0, f=100
}
// 调用测试
int main() {
TestAutoSimplifyCode();
return 0;
}
复习重点(当初记混的规则):
- 当
auto不接引用时:丢了顶层 const(比如const int n→auto f = n推导为int); - 当
auto接引用时:保留 const(比如const auto& r1 = x→推导为const int&); - 别用
auto定义数组(比如auto arr[5] = {1,2,3}会报错),auto只能推导单个变量类型。
2. decltype:变量类型的 “照妖镜”
核心作用:“照” 一下某个表达式的类型,原样抄下来(比auto更精准,保留所有修饰)。当初的坑:不知道decltype和auto的区别,该用decltype时用了auto。
改名后代码(原 test05→TestDecltypeGetType)
cpp
#include <iostream>
#include <typeinfo> // 用于typeid获取类型名
using namespace std;
// 测试decltype:精准获取表达式类型
void TestDecltypeGetType() {
int num1 = 100; // 普通int
double num2 = 200.123; // 普通double
// 场景1:decltype(表达式)→获取表达式的类型
// num1+num2是int+double→结果类型是double,所以num3的类型是double
decltype(num1 + num2) num3;
// 打印num3的类型(typeid.name()返回类型名,不同编译器显示可能不同,比如double显示为double)
cout << "num3的类型是:" << typeid(num3).name() << endl; // 输出:double
// 场景2:decltype vs auto(核心区别)
auto auto_num = num1 + num2; // auto推导为double(只看值类型,和decltype结果一样)
decltype(num1) decl_num = num1; // decltype照抄num1的类型→int
const int const_num = 50;
auto auto_const = const_num; // auto丢const→int
decltype(const_num) decl_const = 60; // decltype保留const→const int(decl_const只读)
}
复习重点:
auto看 “值的类型”,丢顶层 const;decltype看 “表达式本身的类型”,原样保留;- 用在泛型函数返回值(比如
auto add(T t,U u)->decltype(t+u)),推导混合类型的返回值。
3. 右值引用:“捡临时垃圾” 的高效引用
核心作用:绑定到临时值(右值,比如10、a+b),避免不必要的拷贝,提高效率。当初的坑:分不清左值和右值,不知道&&只能绑右值。
改名后代码(原 test06-07→TestRValueReference)
cpp
#include <iostream>
using namespace std;
// 辅助函数:返回两个数的和(返回的是临时值,属于右值)
int AddTwoNum(int v1, int v2) {
return v1 + v2;
}
// 测试右值引用(&&)
void TestRValueReference() {
// 场景1:右值引用绑定字面量(临时值)
const int&& rval_ref1 = 100; // 正确:const右值引用绑字面量(100是右值)
// int&& rval_ref1 = 100; // 也正确:非const右值引用也能绑字面量(C++11后支持)
// 场景2:右值引用绑定函数返回的临时值
int&& rval_ref2 = AddTwoNum(10, 20); // 正确:AddTwoNum返回的30是右值
cout << "rval_ref1=" << rval_ref1 << ", rval_ref2=" << rval_ref2 << endl; // 100 30
// 场景3:错误示范(当初踩过的坑)
int x = 5; // x是左值(有名字、能取地址)
// int&& rval_ref3 = x; // 报错:右值引用不能绑左值!要绑左值用左值引用(&)
int& lval_ref = x; // 正确:左值引用绑左值
// 场景4:用std::move把左值转右值(临时“借”给右值引用)
int y = 10;
int&& rval_ref4 = move(y); // 正确:move把y转成右值
rval_ref4 = 20; // 修改rval_ref4会改y(因为是引用)
cout << "y=" << y << endl; // 输出:20(y被改了)
}
复习重点:
- 左值:有名字、能取地址(比如变量
x);右值:没名字、临时的(比如10、a+b); - 右值引用用
&&,只能绑右值;想绑左值用std::move(但用完左值别再用了,会成 “空壳”)。
4. 智能指针:自动 “还内存” 的管家(再也不怕泄漏)
核心作用:替你管理堆内存,不用手动delete,自动释放,解决内存泄漏和野指针问题。当初的坑:shared_ptr互相引用导致内存泄漏,unique_ptr重复管理同一个指针。
4.1 shared_ptr:可共享的 “共享单车”(原 test15→TestSharedPtrBasic)
cpp
#include <memory>
#include <iostream>
using namespace std;
// 测试shared_ptr基础用法(共享所有权,引用计数)
void TestSharedPtrBasic() {
// 场景1:初始化shared_ptr(推荐用make_shared,比new更安全)
shared_ptr<int> null_ptr1; // 空指针(没管理内存)
shared_ptr<int> null_ptr2 = nullptr;// 空指针(更直观)
shared_ptr<int> ptr3(new int(3)); // 用new初始化(能跑但不推荐)
shared_ptr<int> ptr4 = make_shared<int>(3); // 推荐:make_shared更高效、安全
// 场景2:共享所有权(引用计数+1)
shared_ptr<int> ptr5(ptr4); // ptr5共享ptr4的内存,计数=2
shared_ptr<int> ptr6 = ptr5; // ptr6共享,计数=3
shared_ptr<int> ptr7(move(ptr6)); // ptr7“抢”ptr6的所有权,ptr6变空,计数仍=3
// 场景3:打印指针状态(当初用来调试的)
cout << "ptr1(空):" << ptr1 << endl; // 0(空指针)
cout << "ptr4:" << ptr4 << endl; // 内存地址(非空)
cout << "ptr6(被move后):" << ptr6 << endl; // 0(空了)
cout << "ptr4的引用计数:" << ptr4.use_count() << endl; // 3(ptr4、ptr5、ptr7共享)
}
// 4.2 weak_ptr:解决shared_ptr循环引用的“劝架者”(原test17→TestWeakPtrSolveCycle)
class CB; // 前置声明:CB类在CA后面定义,先告诉编译器有这个类
class CA {
private:
weak_ptr<CB> m_ptr_cb; // 关键:用weak_ptr,不增加引用计数!
public:
CA() { cout << "CA构造:我出生啦!" << endl; }
~CA() { cout << "CA析构:我走啦!" << endl; } // 当初没加weak_ptr时,这里不会执行!
// 设置指向CB的指针
void SetPtrToCB(shared_ptr<CB>& ptr) {
m_ptr_cb = ptr;
}
// 查看weak_ptr的引用计数
void ShowUseCount() {
cout << "CA里的CB引用计数:" << m_ptr_cb.use_count() << endl;
}
};
class CB {
private:
shared_ptr<CA> m_ptr_ca; // 用shared_ptr,会增加引用计数
public:
CB() { cout << "CB构造:我出生啦!" << endl; }
~CB() { cout << "CB析构:我走啦!" << endl; }
// 设置指向CA的指针
void SetPtrToCA(shared_ptr<CA>& ptr) {
m_ptr_ca = ptr;
}
void ShowUseCount() {
cout << "CB里的CA引用计数:" << m_ptr_ca.use_count() << endl;
}
};
// 测试循环引用问题及解决
void TestWeakPtrSolveCycle() {
cout << "=== 开始测试循环引用 ===" << endl;
// 1. 创建两个shared_ptr
shared_ptr<CA> ptr_ca = make_shared<CA>();
shared_ptr<CB> ptr_cb = make_shared<CB>();
cout << "初始计数:CA=" << ptr_ca.use_count() << ", CB=" << ptr_cb.use_count() << endl; // 1 1
// 2. 互相引用(当初的坑:没weak_ptr时,这里会导致循环引用)
ptr_ca->SetPtrToCB(ptr_cb);
ptr_cb->SetPtrToCA(ptr_ca);
cout << "互相引用后计数:CA=" << ptr_ca.use_count() << ", CB=" << ptr_cb.use_count() << endl; // 2 1
// 3. 函数结束时:ptr_ca和ptr_cb销毁
// - ptr_ca销毁:CA的计数从2→1(还被CB的m_ptr_ca持有)
// - ptr_cb销毁:CB的计数从1→0(被销毁),CB的m_ptr_ca也销毁→CA的计数从1→0(被销毁)
// 结果:CA和CB都能正常析构(当初没weak_ptr时,计数会停在1,析构不执行)
cout << "=== 测试结束 ===" << endl;
}
// 4.3 unique_ptr:独占的“专属玩具”(原test17→TestUniquePtrBasic)
void TestUniquePtrBasic() {
// 场景1:unique_ptr独占所有权(不能拷贝,只能move)
int* arr = new int[100]; // 动态数组
unique_ptr<int> ptr_ui(arr); // ptr_ui管理arr
// unique_ptr<int> ptr_ui2(arr); // 报错:不能拷贝,独占所有权!
// unique_ptr<int> ptr_ui2 = ptr_ui; // 也报错:禁止拷贝
// 场景2:用move转移所有权
unique_ptr<int> ptr_ui2 = move(ptr_ui); // 正确:ptr_ui2接管arr,ptr_ui变空
cout << "arr[10] = " << arr[10] << endl; // 输出:随机值(没初始化)
// cout << *ptr_ui << endl; // 报错:ptr_ui是空的!
}
// 4.4 自定义释放规则(原test16→TestSharedPtrCustomDelete)
// 自定义释放函数:释放动态数组(因为默认delete只释放单个元素,数组要delete[])
void DeleteIntArray(int* p) {
cout << "自定义释放:释放动态数组啦!" << endl;
delete[] p; // 数组专用释放
}
void TestSharedPtrCustomDelete() {
// 场景1:用default_delete释放动态数组(标准库提供的数组释放器)
shared_ptr<int> ptr1(new int[10], default_delete<int[]>());
// 场景2:用自定义函数释放动态数组
shared_ptr<int> ptr2(new int[10], DeleteIntArray); // 会调用DeleteIntArray释放
}
复习重点:
shared_ptr:共享所有权,引用计数,最后一个使用者释放;weak_ptr:不增加计数,解决shared_ptr循环引用;unique_ptr:独占所有权,不能拷贝,只能move;- 别用智能指针管理栈内存(比如
shared_ptr<int> p(&x),会重复释放)。
5. Lambda 表达式:临时写的 “小函数”(不用单独定义)
核心作用:当场写一段小代码块,不用单独定义函数,配合sort等算法超方便。当初的坑:捕获列表写错(比如值捕获想改值没加mutable),参数包没展开。
改名后代码(原 test12-14→TestLambdaSortGoods)
cpp
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
// 定义商品结构体(当初用来测试排序的)
struct Goods {
string name; // 商品名
double price; // 价格
};
// 测试Lambda表达式:排序商品(对比仿函数和Lambda)
void TestLambdaSortGoods() {
// 准备商品数据
Goods goods_list[] = { {"苹果",10}, {"香蕉",6.1},{"橙子",7},{"香梨",12} };
int goods_count = sizeof(goods_list) / sizeof(goods_list[0]); // 商品数量
// 场景1:用仿函数排序(C++11前的老方式,麻烦)
struct GoodsPriceComparator {
// 仿函数:重载(),按价格从小到大排序
bool operator()(const Goods& g1, const Goods& g2) {
return g1.price <= g2.price;
}
};
sort(goods_list, goods_list + goods_count, GoodsPriceComparator()); // 传仿函数对象
cout << "场景1:仿函数排序(从小到大)" << endl;
for (auto& goods : goods_list) {
cout << goods.name << " " << goods.price << endl; // 香蕉6.1 橙子7 苹果10 香梨12
}
// 场景2:用Lambda表达式排序(C++11新方式,简洁!)
cout << "\n场景2:Lambda排序(从小到大,更简洁)" << endl;
// Lambda格式:[捕获列表](参数列表)->返回值类型{代码}
auto SortByPriceAsc = [](const Goods& g1, const Goods& g2) -> bool {
return g1.price < g2.price; // 排序规则:价格小的在前
};
sort(goods_list, goods_list + goods_count, SortByPriceAsc);
for (auto& goods : goods_list) {
cout << goods.name << " " << goods.price << endl;
}
// 场景3:Lambda捕获外部变量(当初踩过的坑)
cout << "\n场景3:Lambda捕获外部变量" << endl;
double discount = 0.8; // 外部折扣变量
// 捕获列表[=]:值捕获所有用到的外部变量(discount)
auto CalculateDiscountPrice = [=](const Goods& g) -> double {
return g.price * discount; // 用外部的discount计算折扣价
};
cout << "苹果折扣价:" << CalculateDiscountPrice(goods_list[2]) << endl; // 10*0.8=8
// 场景4:值捕获想改外部变量?加mutable!(当初没加导致报错)
int count = 0;
// [&count]:引用捕获count(改count会影响外部);mutable:值捕获时允许改副本
auto IncrementCount = [&count]() {
count++; // 引用捕获,改的是外部count
};
IncrementCount();
cout << "count=" << count << endl; // 输出:1
}
复习重点:
- Lambda 格式:
[捕获列表](参数列表)->返回值类型{代码}; - 捕获列表:
[=]值捕获所有,[&]引用捕获所有,[x]值捕获 x,[&x]引用捕获 x; - 值捕获想改副本加
mutable,引用捕获改了会影响外部变量。
6. 可变参数模板:能装任意多参数的 “万能口袋”(原 test18-20→TestVariadicTemplate)
核心作用:接收任意数量、任意类型的参数,比如写一个 “打印任意多参数” 的函数。当初的坑:参数包没展开(忘了加...),递归没写终止函数。
改名后代码
cpp
#include <iostream>
using namespace std;
// 场景1:统计参数数量(原test18→TestVariadicTemplateCount)
template<class... Args> // Args:类型参数包(装所有参数的类型)
void PrintParamCount(Args... args) { // args:值参数包(装所有参数的值)
// sizeof...(Args):获取类型参数包的数量
// sizeof...(args):获取值参数包的数量(和类型数量一样)
cout << "参数类型数量:" << sizeof...(Args) << ", 参数值数量:" << sizeof...(args) << endl;
}
// 场景2:递归展开参数包(原test19→TestVariadicTemplateRecursive)
// 递归终止函数:参数包空了的时候调用(必须写!不然递归无限次)
void PrintAllParams() {
cout << "参数包展开完毕!" << endl;
}
// 递归函数:每次拿第一个参数,剩下的继续递归
template<class FirstParam, class... RestParams>
void PrintAllParams(FirstParam first, RestParams... rest) {
cout << "当前参数:" << first << endl; // 打印第一个参数
PrintAllParams(rest...); // 关键:用...展开剩下的参数包(当初忘加...报错!)
}
// 场景3:constexpr展开(C++17后支持,不用写终止函数)
template<class FirstParam, class... RestParams>
void PrintAllParamsConstexpr(FirstParam first, RestParams... rest) {
cout << "当前参数:" << first << endl;
// constexpr:编译期判断参数包是否为空,空了就不递归
if constexpr (sizeof...(rest) > 0) {
PrintAllParamsConstexpr(rest...); // 展开参数包
} else {
cout << "参数包展开完毕!" << endl;
}
}
// 测试函数
void TestVariadicTemplate() {
cout << "场景1:统计参数数量" << endl;
PrintParamCount(1, "hello", 3.14, 'c'); // 输出:4 4
cout << "\n场景2:递归展开参数包" << endl;
PrintAllParams(11, "hello", 22, 'c', 666); // 依次打印5个参数
cout << "\n场景3:constexpr展开参数包" << endl;
PrintAllParamsConstexpr("hello", 11, "world", 22.2); // 依次打印4个参数
}
复习重点:
- 参数包用
...表示(定义时打包,使用时展开); - 递归展开必须写 “终止函数”(空参数版本);
- C++17 后用
constexpr展开,不用写终止函数,更简单。
7. 新增容器:array(固定数组)和 forward_list(单向链表)和无序容器
核心作用:array是 “带成员函数的数组”(固定大小,安全),forward_list是高效的单向链表。当初的坑:array没加<array>头文件导致编译报错,forward_list用错push_back(它只有push_front)。
7.1 array:固定大小的 “安全数组”(原 test22→TestArrayBasic)
cpp
#include <array>
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;
// 测试array容器(固定大小,比普通数组安全)
void TestArrayBasic() {
// 场景1:创建array(必须指定类型和大小,大小是编译期常量)
array<int, 5> arr1; // 空array(值是随机的,没初始化)
cout << "场景1:空array(随机值)" << endl;
for (auto it : arr1) {
cout << it << " "; // 输出随机值(比如0 0 4200128 0 4200064)
}
cout << endl;
// 场景2:初始化array(部分或全部)
array<int, 10> arr2 = { 1,2,3,4,5,6,7,8,9,10 }; // 全部初始化
array<string, 10> arr3 = { "hello","world" }; // 部分初始化(剩下的是空字符串)
cout << "\n场景2:初始化array" << endl;
for (auto it : arr3) {
cout << (it.empty() ? "[空]" : it) << " "; // hello world [空]...
}
cout << endl;
// 场景3:array的安全访问(比普通数组[]安全)
cout << "\n场景3:array安全访问" << endl;
cout << "arr2[0] = " << arr2[0] << endl; // 普通访问(越界不报错,危险)
try {
cout << "arr2.at(1) = " << arr2.at(1) << endl; // 安全访问(越界抛异常)
// cout << "arr2.at(10) = " << arr2.at(10) << endl; // 越界:抛out_of_range异常
} catch (const out_of_range& e) {
cout << "访问出错:" << e.what() << endl;
}
// 场景4:array的其他常用函数
cout << "\n场景4:array常用函数" << endl;
cout << "arr2的第2个元素(get<2>):" << get<2>(arr2) << endl; // 编译期访问,必须用常量索引
cout << "arr2的第一个元素(front):" << arr2.front() << endl; // 1
cout << "arr2的最后一个元素(back):" << arr2.back() << endl; // 10
}
// 7.2 forward_list:高效的单向链表(原test23→TestForwardListBasic)
#include <forward_list>
using namespace std;
void TestForwardListBasic() {
// 场景1:创建forward_list(单向链表,只能从头部操作高效)
forward_list<int> empty_list; // 空链表
forward_list<int> ten_zeros(10); // 10个0的链表
forward_list<int> five_eleven(5, 11); // 5个11的链表
forward_list<int> copy_list(five_eleven); // 拷贝构造
// 场景2:从其他容器初始化
int arr[] = { 1,2,76,4,5 };
forward_list<int> from_array(arr, arr + 5); // 从数组初始化
array<int, 10> arr2 = { 66,33,55,99,100,11,22,3,11,13 };
forward_list<int> from_array2(arr2.begin(), arr2.end()); // 从array初始化
// 场景3:forward_list的常用操作(注意:没有push_back!单向链表尾部操作低效)
forward_list<int>& list = from_array; // 引用,方便操作
list.push_front(100); // 头部插入(高效)
list.emplace_front(666); // 头部构造(比push_front高效,直接在头部创建元素)
cout << "场景3:头部插入后第一个元素" << endl;
cout << "list.front() = " << list.front() << endl; // 666
// 场景4:排序和遍历
list.sort(); // 链表排序(forward_list有自带sort)
cout << "\n场景4:排序后遍历" << endl;
for (auto it : list) {
cout << it << " "; // 1 2 4 5 100 666 76(注意:76是原数组的,排序后在最后)
}
cout << endl;
// 场景5:错误示范(当初踩过的坑)
// list.push_back(200); // 报错:forward_list没有push_back!要尾部插入用insert_after(低效)
}
复习重点:
array:固定大小,编译期确定,有at()(安全访问)、front()、back()等函数;forward_list:单向链表,只有push_front(高效),没有push_back,排序用自带sort();- 用
array要加<array>头文件,用forward_list要加<forward_list>头文件(当初忘加报错)。
8、无序容器 —— 比有序容器快的 “菜市场”
先给无序容器打个比方:传统set/map是 “超市排队结账”(按顺序排好,找东西慢但整齐),而 C++11 的unordered_*是 “菜市场摊位”(随便摆,找东西快但乱)。它们用 “哈希表” 实现,核心优势是插入、查找、删除速度快(平均 O (1)),缺点是 “不排序”—— 适合只关心 “有没有”“找不找得到”,不关心顺序的场景。
8.1. unordered_set:无序且去重的 “单身俱乐部”(原 test24→TestUnorderedSetBasic)
核心特点:像个 “只收单身汉的俱乐部”—— 元素无序,且重复元素进不来(来了也被门卫拦住)。
改名后代码(附爆笑注释)
cpp
运行
#include <unordered_set> // 必须加!不然编译器不认识unordered_set
#include <iostream>
using namespace std;
// 测试unordered_set基础用法:无序、去重、常用操作
void TestUnorderedSetBasic() {
// 1. 空容器构造:建了个空的“单身俱乐部”,还没成员
unordered_set<int> empty_set;
// 2. 拷贝构造:复制别人的空俱乐部,结果还是空的(白忙活)
unordered_set<int> copy_empty_set(empty_set);
// 3. 迭代器构造:用空容器的迭代器造新容器,还是空的(更白忙活)
unordered_set<int> iter_set(empty_set.begin(), empty_set.end());
// 4. 从数组构造:终于有成员了!把数组里的1-5塞进俱乐部
int num_arr[] = { 1,2,3,4,5 };
unordered_set<int> arr_set(num_arr, num_arr + 5); // 俱乐部成员:1,2,3,4,5(顺序不一定,看哈希)
// 5. 移动构造:“抢”别人的成员!把copy_empty_set的家当搬过来(但它是空的,还是白抢)
unordered_set<int> move_set(move(copy_empty_set));
// 6. 初始化列表构造:重点!故意写两个66,看会不会被拦住
unordered_set<int> test_set = { 11,66,22,55,66,33 };
// 这里当初懵过:明明写了两个66,为啥最后只有一个?
// 答:俱乐部“不接受重复成员”,第二个66被门卫拦下了!
// 7. 判断容器是否为空:empty()返回bool(0=非空,1=空)
cout << "test_set是否为空:" << test_set.empty() << endl; // 输出0(非空)
// 8. 查找元素:find(值)返回迭代器,找到就指向该元素,找不到指向end()
auto find_iter = test_set.find(11);
// cout << find_iter << endl; // 报错!迭代器不能直接打印,得解引用
cout << "找到的元素:" << *find_iter << endl; // 输出11(找到11了)
// 9. 统计元素个数:count(值)——重点!当初的坑就在这
cout << "test_set中66的个数:" << test_set.count(66) << endl;
// 输出1!不是2!因为重复元素被去重了,count只会返回0(没找到)或1(找到)
// 10. 插入元素:三种常用方式
test_set.insert(100); // 插入单个元素(100加入俱乐部)
test_set.insert({ 200,300,400,500 });// 插入初始化列表(一次加4个)
test_set.insert(test_set.begin(), 999);// 插入到迭代器位置(但无序,插哪都一样)
test_set.insert(arr_set.begin(), arr_set.end());// 插入另一个容器的元素(1-5,重复的被拦住)
// 11. 遍历容器:虽然无序,但能打印所有成员
cout << "test_set所有成员(顺序不一定):";
for (auto& item : test_set) {
cout << item << " ";
// 可能输出:11 66 22 55 33 100 200 300 400 500 999 1 2 3 4 5(顺序看哈希)
}
cout << endl;
}
当初踩过的坑(重点复习):
- 必须加头文件:
#include <unordered_set>,不然编译器报 “未定义类”(当初忘加,查了半小时错); - count () 返回 0 或 1:不管你插多少次重复元素,它只记 “有或没有”,别指望返回 2;
- 迭代器不能直接打印:
find()返回的是迭代器,得用*解引用才能拿到值(当初直接cout << find_iter报错)。
8.2. unordered_multiset:无序且可重复的 “大家庭”(原 test25→TestUnorderedMultisetBasic)
核心特点:像个 “欢迎多胞胎的大家庭”—— 元素无序,但允许重复(来了都能进),适合需要统计重复元素个数的场景。
改名后代码
cpp
运行
#include <unordered_set>
#include <iostream>
using namespace std;
// 测试unordered_multiset:无序、允许重复、常用操作
void TestUnorderedMultisetBasic() {
// 1. 初始化列表构造:故意加两个11、两个33,看会不会都进去
unordered_multiset<int> multi_set = { 11,33,11,22,55,66,99,33 };
// 成员:11,33,11,22,55,66,99,33(顺序不一定,重复的都在)
// 2. 遍历方式1:范围for(懒人最爱,不用管迭代器)
cout << "范围for遍历:";
for (auto& it : multi_set) {
cout << it << " "; // 输出:11 11 33 33 22 55 66 99(顺序看哈希)
}
cout << endl;
// 3. 遍历方式2:迭代器(传统写法,适合需要中途停止的场景)
cout << "迭代器遍历:";
for (auto item = multi_set.begin(); item != multi_set.end(); item++) {
cout << *item << " "; // 和上面输出类似,就是顺序可能不同
}
cout << endl;
// 4. 常用查询函数
cout << "是否为空:" << multi_set.empty() << endl; // 0(非空)
cout << "最大能装多少元素(理论值):" << multi_set.max_size() << endl; // 很大的数(比如4611686018427387903)
cout << "当前元素个数(算重复):" << multi_set.size() << endl; // 8(两个11+两个33+其他4个)
// 5. 插入元素:插入1000,现在size变成9
multi_set.insert(1000);
cout << "插入1000后size:" << multi_set.size() << endl; // 9
}
重点区别(vs unordered_set):
size()算重复元素:unordered_set插两个 11,size 是 1;unordered_multiset插两个 11,size 是 2;count(值)返回实际个数:比如multi_set.count(11)会返回 2(当初要是早知道,就不用疑惑 “为什么 count 不是 2” 了)。
8.3. unordered_map:无序键值对的 “快递柜”(原 test26→TestUnorderedMapBasic)
核心特点:像个 “按手机号存快递的柜子”—— 每个 “键(key)” 对应一个 “值(value)”,键不能重复(一个手机号只能对应一个柜子),无序但找东西快(输手机号直接定位柜子)。
改名后代码(附 bug 修复)
cpp
运行
#include <unordered_map>
#include <iostream>
#include <string>
using namespace std;
// 测试unordered_map:无序键值对、常用操作、bug修复
void TestUnorderedMapBasic() {
// 1. 空容器构造:建了个空快递柜,没手机号没快递
unordered_map<string, int> empty_map;
// 2. 初始化列表构造:重点!键是姓名(string),值是分数(int)
unordered_map<string, int> score_map = {
{"江桂东", 666},
{"张三", 222},
{"李四", 333}
}; // 快递柜:江桂东→666,张三→222,李四→333
// 3. 拷贝构造:复制score_map的快递柜
unordered_map<string, int> copy_map(score_map);
// 4. 迭代器构造:用score_map的迭代器造新柜子
unordered_map<string, int> iter_map(score_map.begin(), score_map.end());
// 5. typedef简化类型(当初嫌类型太长,用typedef偷懒)
typedef unordered_map<string, int> NameScoreMap; // 给类型起个别名:NameScoreMap
NameScoreMap alias_map = {
{"江桂东", 12138666}, // 键重复了?会覆盖前面的!
{"张三", 222},
{"李四", 333}
}; // 江桂东的分数变成12138666(键重复会覆盖旧值)
// 6. 当初的bug代码!错误示范:初始化列表格式错了
// NameScoreMap bug_map = { {"abc",666},"nb",12138 };
// 错因:每个键值对都要包在{}里,不能直接写"nb",12138
// 修复后代码:
NameScoreMap fixed_map = { {"abc", 666}, {"nb", 12138} }; // 正确!每个键值对都有{}
// 7. 遍历map:用范围for,it是pair类型(first=键,second=值)
cout << "iter_map的所有键值对:" << endl;
for (auto it : iter_map) {
cout << it.first << ":" << it.second << endl;
// 输出:江桂东:666 张三:222 李四:333(顺序看哈希)
}
cout << endl;
// 8. 访问值的两种方式(重点!当初分不清)
// 方式1:[]访问(简单,但键不存在时会自动插入空值)
cout << "江桂东的分数([]访问):" << score_map["江桂东"] << endl; // 666
// 方式2:at()访问(安全,键不存在时抛异常,不会乱插空值)
cout << "江桂东的分数(at()访问):" << score_map.at("江桂东") << endl; // 666
cout << "张三的分数(at()访问):" << score_map.at("张三") << endl; // 222
// 错误示范:访问不存在的键
// cout << score_map.at("王五") << endl; // 抛异常:out_of_range
// cout << score_map["王五"] << endl; // 不抛异常,但会插入"王五":0(坑!)
}
当初踩过的坑:
- 初始化列表格式:每个键值对必须用
{}包起来,不能直接写{"a",1}"b",2(当初漏写 {} 报错); - [] 和 at () 的区别:
[]会自动插入不存在的键(值为默认 0),at()会抛异常 —— 推荐用at(),避免不小心插入垃圾数据; - 键重复会覆盖:比如插两次
{"江桂东", 666}和{"江桂东", 123},最后值是 123(旧值被覆盖)。
8.4. unordered_multimap:无序可重复键的 “多格快递柜”(原 test27→TestUnorderedMultimapBasic)
核心特点:像个 “一个手机号能对应多个柜子的快递柜”—— 键可以重复(一个姓名能对应多个分数),适合 “一对多” 的场景(比如一个人多次考试的分数)。
改名后代码
cpp
运行
#include <unordered_map>
#include <iostream>
#include <string>
using namespace std;
// 测试unordered_multimap:无序、键可重复、常用操作
void TestUnorderedMultimapBasic() {
// 初始化列表构造:故意让“江桂东”对应两个分数(666和888)
unordered_multimap<string, int> multi_score_map = {
{"江桂东", 666},
{"张三", 222},
{"江桂东", 888}, // 键重复了,能进去!
{"李四", 333}
}; // 键值对:江桂东→666,张三→222,江桂东→888,李四→333
// 遍历:和map一样,用范围for,it.first是键,it.second是值
cout << "multi_score_map的所有键值对:" << endl;
for (auto& it : multi_score_map) {
cout << it.first << ":" << it.second << endl;
// 输出:江桂东:666 江桂东:888 张三:222 李四:333(顺序看哈希)
}
}
重点区别(vs unordered_map):
- 键可以重复:
unordered_map键重复会覆盖,unordered_multimap键重复会新增; - 没有 [] 访问:因为一个键对应多个值,
[]不知道该返回哪个,所以只能用find()或equal_range()找值(这个后面学,先记住 “不能用 []”)。
9. 其他小特性:范围 for、默认 / 删除函数
9.1 范围 for:遍历容器的 “懒人语法”(原 test10→TestRangeBasedFor)
cpp
#include <vector>
#include <iostream>
using namespace std;
void TestRangeBasedFor() {
vector<int> num_vec = { 1,2,3,4,5,6 };
cout << "范围for遍历vector:" << endl;
// 原写法:for (vector<int>::iterator it = num_vec.begin(); it != num_vec.end(); it++)
for (int num : num_vec) { // 范围for:直接遍历每个元素,不用写迭代器
cout << num << " "; // 1 2 3 4 5 6
}
cout << endl;
// 遍历数组也能用
int arr[] = { 10,20,30 };
cout << "范围for遍历数组:" << endl;
for (int num : arr) {
cout << num << " "; // 10 20 30
}
}
9.2 默认 / 删除函数:控制构造 / 赋值(原 test21→TestDefaultDeleteFunction)
cpp
#include <iostream>
using namespace std;
class TestDefaultDelete {
private:
int n;
public:
// 1. =default:让编译器生成默认版本(比自己写的高效)
TestDefaultDelete() = default; // 默认构造函数(编译器自动生成)
TestDefaultDelete(int num) : n(num) {} // 自定义构造函数
// 2. =delete:禁止编译器生成某个函数(比如禁止拷贝构造)
TestDefaultDelete(const TestDefaultDelete&) = delete; // 禁止拷贝构造(不能用=拷贝)
// 3. 赋值运算符:用=default生成默认版本
TestDefaultDelete& operator=(const TestDefaultDelete&) = default;
};
void TestDefaultDeleteFunction() {
TestDefaultDelete obj1; // 正确:调用默认构造
TestDefaultDelete obj2(10); // 正确:调用自定义构造
// TestDefaultDelete obj3(obj2); // 报错:拷贝构造被delete了!
TestDefaultDelete obj4;
obj4 = obj2; // 正确:赋值运算符是default的,能用
}
三、复习速查表(怕忘就看这个!)
| 特性 | 核心作用 | 关键语法 / 避坑点 |
|---|---|---|
| auto | 自动推导变量类型,简化长类型书写(如迭代器) |
1. 类型不为引用时,自动丢弃顶层 const; 2. 不能用于定义数组(如 3. 当类型为引用时,保留表达式的 const 属性 |
| decltype | 精准反射表达式的原生类型,不忽略 const、引用等修饰符 |
1. 完全保留表达式的所有类型修饰(如 2. 常用于泛型函数的尾置返回类型(如 3. 仅分析表达式类型,不执行表达式 |
| 右值引用 | 绑定到右值(临时值、字面量等),实现移动语义,避免不必要的拷贝 |
1. 用 2. 仅能绑定右值,绑定左值需用 3. |
| shared_ptr | 共享堆内存所有权,通过引用计数自动释放内存,避免内存泄漏 |
1. 用 2. 引用计数为 0 时自动释放内存; 3. 避免循环引用(需配合 4. 不管理栈内存(如 |
| weak_ptr | 作为shared_ptr的观察者,解决其循环引用问题,不拥有内存所有权 |
1. 不增加 2. 需通过 3. 用 4. 不能直接用 |
| unique_ptr | 独占堆内存所有权,禁止拷贝,仅支持移动,轻量高效 |
1. 不能拷贝(如 2. 不允许多个 3. 推荐用 |
| Lambda | 临时定义匿名函数片段,无需单独声明函数,灵活配合算法(如sort、for_each) |
1. 基础格式: 2. 捕获列表: 3. 配合 4. 可赋值给同类型函数指针 |
| 可变参数模板 | 定义支持任意数量、任意类型参数的模板(函数 / 类),实现泛型灵活性 |
1. 用 2. 需通过递归展开(需写终止函数)或 C++17 折叠表达式(如 3. 用 |
| array | 固定大小的安全数组,兼具普通数组的高效和容器的成员函数(如at()) |
1. 需包含 2. 大小为编译期常量(如 3. 用 4. 支持 |
| forward_list | 单向链表容器,仅支持从头部高效操作,内存开销小 |
1. 需包含 2. 无 3. 自带 4. 遍历需用前向迭代器,不能反向遍历 |
| unordered_set(无序去重集合) | 无序存储元素,自动去重,基于哈希表实现快速插入 / 查找 / 删除(平均 O (1)) |
1. 需包含 2. 3. 元素无序,不支持排序操作; 4. 插入重复元素时会自动忽略 |
| unordered_multiset(无序可重复集合) | 无序存储元素,允许重复,支持统计重复元素个数,哈希实现高效操作 |
1. 需包含 2. 3. 4. 元素无序,插入顺序不影响存储顺序; 5. 插入重复元素会保留所有副本 |
| unordered_map(无序键值对容器) | 无序存储键值对(键唯一,一个键对应一个值),快速通过键索引值(哈希实现) |
1. 需包含 2. 初始化列表中每个键值对需用 3. 访问值优先用 4. 键重复时,后插入的会覆盖前值; 5. 遍历用 |
| unordered_multimap(无序可重复键键值对容器) | 无序存储键值对(键可重复,一个键对应多个值),支持 “一对多” 映射 |
1. 需包含 2. 无 3. 需通过 4. 键可重复,插入多个相同键会生成多个独立键值对;5. |
四、结尾:以后复习别再 “猜代码” 了!
这篇笔记把当初的 “test01” 全改成了 “看得懂” 的函数名,加了注释,还吐槽了踩过的坑。以后复习时,对着速查表看特性,对着代码看用法,不用再对着 “密码式” 函数名发呆了。
C++11 新特性看着多,但核心都是 “让代码更简洁、更安全”—— 比如 auto 少写类型,智能指针少管内存,Lambda 少写函数。记住这些 “偷懒又安全” 的原则,复习起来会更轻松~
源码分享,欢迎各位自由修改使用。
头文件:
#pragma once
#include <vector>
#include <iostream>
#include <typeinfo>
#include <map>
#include <algorithm>
#include <functional>
using namespace std;
/*
函数声明
*/
//指针函数
void (*FP)();
void test11();
void test12();
void test13();
void test14();
#pragma once
#include <vector>
#include <iostream>
#include <typeinfo>
#include <map>
using namespace std;
/*
函数声明
*/
//auto关键字
void test01();
//
void test02();
//
auto func1(int v1, int v2);
//
void test03();
//
void test04();
//
void test05();
void test06();
void test07();
void test08();
void test09();
//for each
void test10();
//
template<class T>
class Student;
#pragma once
#include <vector>
#include <iostream>
#include <typeinfo>
#include <map>
#include <algorithm>
#include <functional>
#include <memory> //智能指针头文件
using namespace std;
/*
函数声明
*/
void test18();
//参数包展开
void test19();
//void test20();
void test21();
#pragma once
#include <vector>
#include <iostream>
#include <typeinfo>
#include <map>
#include <algorithm>
#include <functional>
#include <array>
#include <forward_list>
#include <unordered_map>
#include <unordered_set>
using namespace std;
/*
函数声明
*/
void test22();
void test23();
void test24();
void test25();
void test26();
void test27();
void test28();
#pragma once
#include <vector>
#include <iostream>
#include <typeinfo>
#include <map>
#include <algorithm>
#include <functional>
#include <memory> //智能指针头文件
using namespace std;
/*
函数声明
*/
void test15();
//自定义释放规则
void test16();
void test17();
源文件:
// C++11常用新特性.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include "myCode.h"
using namespace std;
int main() {
//test01();
//test02();
//test03();
//test04();
//test05();
//test06();
//test07();
//test08();
//test09();
test10();
return 0;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
#include "Lambda表达式.h"
class Rate {
private:
double _rate;//利率
public:
Rate(double rate) :_rate(rate) {
}
//仿函数
double operator()(double money, int year) {
return money * this->_rate * year;
}
};
//主函数测试用
int main() {
//test11();
//test12();
//test13();
test14();
return 0;
}
/*
函数定义实现
*/
struct Goods {
string name;
double price;
};
struct Compare {
bool operator()(const Goods& gl, const Goods& gr) {
return gl.price <= gr.price;//按照价格从小到大排序
}
};
void test14() {
Goods gds[] = { {"苹果",10}, {"香蕉",6.1},{"橙子",7},{"香梨",12}};
sort(gds, gds + sizeof(gds) / sizeof(gds[0]), Compare());
for (auto it : gds) {
cout << it.name << " " << it.price << endl;
}
cout << endl << "使用lambda表达式修改" << endl;
//使用lambda表达式修改
auto fun0=[](const Goods& gl, const Goods& gr)->bool {
return gl.price < gr.price;
};
sort(gds, gds + sizeof(gds) / sizeof(gds[0]), fun0);
for (auto it : gds) {
cout << it.name << " " << it.price << endl;
}
}
void test13() {
int array[] = { 2,4,15,3,9,8,7,0 };
//默认从小到大排序 升序排序
sort(array, array + sizeof(array) / sizeof(array[0]));
for (int a : array) {
cout << a << " ";
}
cout << endl;
//修改sort的排序规则
//sort(array, array + sizeof(array) / sizeof(array[0]),greater<int>);
}
void test12() {
int num1 = 3;
int num2 = 4;
//省略参数列表和返回值类型
auto fun0 = [&num1, &num2] {
return num1 + num2;
};
cout << fun0() << endl;
//lambda表达式 省略返回值类型,返回值类型由编译器推导为int
auto fun1 = [&num1, &num2](int num3) {
num2 = num1 + num3;
};
fun1(300);
cout << "num1=" << num1 << " " << "num2=" << num2 << endl;
//捕捉列表可以是lambda表达式
auto fun2 = [fun1] {
cout << "hello lambda" << endl;
};
fun2();
//各部分比较完善的lambda表达式
//auto fun3 = [=, &num1](int num3)->int { bug代码
auto fun3 = [=, &num1](int num3)mutable->int {
num2 += num1 * num3;
return num1 * num1 + num3;
};
cout << fun3(11) << endl;
auto fun4 = [&num1, &num2](int num3) {
num2 = num1 + num3;
return num2;
};
//fun1 = fun4; bug代码
//赋值捕获
int v1 = 10;
auto fun5 = [v1](int v2) mutable{
v1 = v1 * 3;
return v2 + v1;
};
cout << fun5(2) << endl;
//允许使用一个lambda表示拷贝构造一个新的副本
auto fun6(fun5);
cout << fun6(10) << endl;
//可以将lambda表达式赋值给相同类型的函数指针
auto fun7 = [] {};
FP = fun7;
FP();
//仿函数的方式
double rate = 0.8;
Rate r1(rate);
double rd = r1(20000, 2);
cout << rd << endl;
//匿名函数方式(lambda表达式的方式)
auto r2 = [=](double money, int year)->double {
return money * rate * year;
};
double rd2 = r2(20000, 2);
cout << rd2 << endl;
}
void test11() {
//最简单的kambda表达式,该lambda表达式没有任何意义
[] {};
}
#include "myCode.h"
/*
函数定义实现
*/
void test10() {
vector<int> v1 = { 1,2,3,4,5,6 };
for (int num : v1) {
cout << num << " ";
}
}
class ClassNum {
private:
int _x;
int _y;
public:
ClassNum(int n1 = 0, int n2 = 0) {
this->_x = n1;
this->_y = n2;
}
void printInfo() {
cout << this->_x << " " << this->_y << endl;
}
};
void test09() {
vector<int> vc{ 1,2,3,4,5,6 };
int num1 = { 200 };
int num2{ 100 };
cout << num1 << endl;
cout << num2 << endl;
//数组
int arr1[] = { 1,2,3,4,5 };
int arr2[]{ 2,3,4,5,6 };
vector<int> {1, 2, 3, 4, 5, 6};
map<int ,int> mp1{ {1,11},{2,22},{3,33} };
ClassNum cn1{ 1,2};
cn1.printInfo();
}
int num1 = 100;
int& returnNum() {
return num1;
}
void test08() {
int num2 = 10;
int num3 = num2;
int* p1 = new int(10);
const int num4 = 20;
//num4 = num1;
cout << "&num4 :" << &num4 << endl;
//num2 + 100 = 300;
returnNum() = 999;
//int& num3 = 999;
cout << returnNum() << endl;
}
template<class T>
//
class Student {
private:
//非静态成员变量不能使用auto
//auto name;
//auto age;
int age;
};
int func2(int v1, int v2) {
return v1 + v2;
}
void test07() {
const int&& num1 = 100;
int&& num2 = func2(10, 20);
cout << num1 << endl;
cout << num2 << endl;
}
//
auto func1(int v1, int v2) {
return v1 + v2;
}
//模版函数
template<class R,class T,class U>
auto add(T t, U u) ->decltype(t+u){ //尾随返回类型
return t + u;
}
void test06() {
int v1 = 100;
double v2 = 2.323;
auto v3 = add<decltype(v1 + v2)>(v1, v2);
cout << v3 << endl;
int a = 10;
int& ref_a = a;
//int& ref_b = 20;
int&& ref_c = 20;
cout << ref_c << endl;
}
void test05() {
auto num1 = 100;
auto num2 = 200.123;
decltype(num1 + num2) num3;
//num3 = num1 + num2;
cout << typeid(num3).name() << endl;
}
//
void test04() {
int arr[1000] = { 1,2,3 };
auto arr2 = arr;
//auto arr3[1] = arr[1];
cout << arr2 << endl;
cout << arr2[1] << endl;
}
//
void test03() {
Student<int> s1;
//Student<auto> s2; //不能使用auto
}
//
void test02() {
cout << func1(100, 200);
}
//auto关键字
void test01() {
vector<int> vc = { 1,2,3,4,51 };
//使用迭代器遍历向量vc
//for (vector<int>::iterator it = vc.begin(); it != vc.end(); it++) {
for (auto it = vc.begin(); it != vc.end(); it++) {
cout << *it << " ";
}
cout << endl;
int x = 0;
const auto n = x;
auto f = n;
const auto& r1=x;
auto& r2 = r1;
//看看是否能修改
x = 100;
//n = 100; //n不能修改,只读
f = 100; //f可以修改,不是const修饰的
//r1 = 100; //r1不可修改变量,const属性 只读
//r2 = 100; //r2不可修改变量,const属性,只读
}
/*
auto关键字总结:
总结:
1.当类型不为引用时,auto的推导结果将不保留表达式的const属性:
2.当类型为引用时,auto的推导结果将保留表达式的const属性。
*/
#include "可变参数模版.h"
//主函数
int main() {
//test18();
//test19();
//test20();
test21();
return 0;
}
/*
函数定义实现
*/
class ClassTest {
private:
int n;
public:
ClassTest() = default;
ClassTest(int num) :n(num) {}
ClassTest(const ClassTest&) = delete;
ClassTest& operator=(const ClassTest& ct);
};
ClassTest& ClassTest::operator=(const ClassTest& ct) = default;
void test21() {
ClassTest ct1;
}
template<class...T>
void func11(T...args) {
cout << "参数值的数量:" << sizeof...(args) << endl;
cout <<"参数类型的数量:" << sizeof...(T) << endl;
}
void test18() {
func11(1,2,3,4,5,6,7,8,9,10);
}
//无参数的时候终止
void funcName111() {
cout << "递归函数终止" << endl;
}
template<class T,class...U>
void funcName111(T first, U... other) {
cout << "接收到的参数:" << first << endl;
funcName111(other...);
}
//参数包展开
void test19() {
funcName111(11, "hello", 22, 'c', 666);
}
template<class T, class... U>
void funcName112(T first, U... args) {
cout << "接收到的参数:" << first << endl;
if constexpr (sizeof...(args) > 0) {
funcName112(args...);
}
}
void test20() {
funcName112("hello", 11, "world", 22.2, 33, 44, 55, 66);
}
#include "新增容器.h"
int main() {
//test22();
//test23();
//test24();
//test25();
//test26();
test27();
test28();
return 0;
}
/*
函数定义声明
*/
int getCharNum(string s, char c) {
int count = 0;
for_each(s.begin(), s.end(), [&](char ch) {
if (c == ch) {
count++;
}
});
return count;
}
void test28() {
string s1;
//getline(cin, s1);
char ch;
cin >> s1 >> ch;
cout << getCharNum(s1, ch) << endl;
}
void test27() {
unordered_multimap<string, int> umm1 =
{ {"江桂东",666} ,{"张三",222} ,{"李四",333} };
for (auto& it : umm1) {
cout << it.first << ":" << it.second << endl;
}
}
void test26() {
//空的map
unordered_map<string, int> mp1;
//初始化列表
unordered_map<string, int> mp2 =
{ {"江桂东",666} ,{"张三",222} ,{"李四",333} };
//拷贝构造函数
unordered_map<string, int> mp3(mp2);
//迭代器初始化
unordered_map<string, int> mp4(mp2.begin(), mp2.end());
//拷贝初始化
typedef unordered_map<string, int> mp5;
mp5 mp6 = { {"江桂东",12138666} ,{"张三",222} ,{"李四",333} };
//mp5 mp7 = { {"abc",666},"nb",12138 }; bug代码
//修改后的正确代码
mp5 mp7 = { {"abc",666},{"nb",12138} };
mp5 mp8;
//merge(mp6.begin(), mp6.end(), mp7.begin(), mp7.end(), mp8.begin()); 有bug
mp5 mp9 = mp8;
for (auto it : mp4) {
cout << it.first << ":" << it.second << endl;
}
cout << endl;
cout << mp2["江桂东"] << endl;
cout << mp2.at("江桂东") << endl;
cout << mp2["张三"] << endl;
}
void test25() {
unordered_multiset<int> um1 = { 11,33,11,22,55,66,99,33 };
//简便遍历方法
for (auto& it : um1) {
cout << it << " ";
}
cout << endl;
for (auto item = um1.begin(); item != um1.end();item++) {
cout <<*item << " ";
}
cout << endl;
cout << um1.empty() << endl;
cout << um1.max_size() << endl;
cout << um1.size() << endl;
um1.insert(1000);
}
void test24() {
//空的set
unordered_set<int> set1;
//通过拷贝构造函数
unordered_set<int> set2(set1);
//迭代器构造
unordered_set<int> set3(set1.begin(), set1.end());
int arr[] = { 1,2,3,4,5 };
unordered_set<int> set4(arr, arr + 5);
unordered_set<int> set5(move(set2));
unordered_set<int> set6 = { 11,66,22,55,66,33 };
cout << set6.empty() << endl;
auto it = set6.find(11);
//cout << set6.find(11) << endl;
cout << *it << endl;
//set6中有两个66但是就是不会输出2,这是为什么呢?真的是好伤脑筋啊!
//解释:当然是因为unordered虽然是无序集合,但是他也是集合,这个集合不允许重复元素出现,有多个也只是留下一个幸运儿哦,所以set6.count(66)只能输出0或1
cout << set6.count(66) << endl;
set6.insert(100);
set6.insert({ 200,300,400,500 });
set6.insert(set6.begin(), 999);
set6.insert(set4.begin(), set4.end());
for (auto& item : set6) {
cout << item << " ";
}
cout << endl;
}
void test23() {
//创建一个空的单向链表
forward_list<int> value1;
//包含n个元素的单向链表
forward_list<int> v2(10);
//创一个包含n个元素的单向链表并指定初始值
forward_list<int> v3(5, 11);
//通过拷贝构造函数创建
forward_list<int> v4(v3);
//拷贝其他类型的容器
int arr_num[] = { 1,2,76,4,5 };
forward_list<int> v5(arr_num, arr_num + 5);
for (auto it : v5) {
cout << it << " ";
}
cout << endl;
array<int, 10> arr_num2 = { 66,33,55,99,100,11,22,3,11,13 };
forward_list<int> v6(arr_num2.begin(), arr_num2.end());
for (auto it : v6) {
cout << it << " ";
}
cout << endl;
v6.push_front(100);
v6.emplace_front(666);//效率更高
cout << v6.front() << endl;
cout << endl;
v6.sort();
v5.sort();
for (auto item : v5) {
cout << item << " ";
}
}
void test22() {
//固定的数据类型和长度
array<int, 5> arr1;
for (auto it : arr1) {
cout << it << " ";
}
cout << endl;
//给部分或者全部的初始值
array<int, 10> arr2 = { 1,2,3,4,5,6,7,8,9,10};
array<string, 10> arr3 = { "hello","word" };
for (auto it : arr3) {
cout << it << " ";
}
cout << endl;
const int len = 10;
array<double, len> arr4;
cout << arr2[0] << endl;
try {
cout << arr2.at(1) << endl;
}
catch (exception e) {
cout << e.what() << endl;
}
int x = get<2>(arr2);
cout << x << endl;
cout << get<1>(arr2) << endl;
cout << arr2.front() << endl;
cout << arr2.back() << endl;
}
#include "智能指针.h"
class CB;
class CA {
private:
weak_ptr<CB> m_ptr_b;
public:
CA(){
cout << "CA() called" << endl;
}
~CA() {
cout<< "~CA() called" << endl;
}
void set_ptr(shared_ptr<CB>& ptr) {
m_ptr_b = ptr;
}
void m_ptr_b_use_count() {
cout << "m_ptr_b use count:" << m_ptr_b.use_count() << endl;
}
void show() {
cout << "this is class CA" << endl;
}
};
class CB {
private:
shared_ptr<CA> m_ptr_a;
public:
CB() {
cout << "CB() called" << endl;
}
~CB() {
cout << "~CB() called" << endl;
}
void set_ptr(shared_ptr<CA>& ptr) {
m_ptr_a = ptr;
}
void m_ptr_a_use_count() {
cout << "m_ptr_a use count:" << m_ptr_a.use_count() << endl;
}
void show() {
cout << "this is class CB" << endl;
}
};
//主函数
int main() {
//test15();
//test16();
test17();
return 0;
}
/*
函数定义实现
*/
void test17() {
//析构成功
shared_ptr<CA> ptr_ca(new CA());
shared_ptr<CB> ptr_cb(new CB());
cout << "ca use count:" << ptr_ca.use_count() << endl;
cout << "cb use count:" << ptr_cb.use_count() << endl;
//析构失败
ptr_ca->set_ptr(ptr_cb);
ptr_cb->set_ptr(ptr_ca);
cout << "ca use count:" << ptr_ca.use_count() << endl;
cout << "cb use count:" << ptr_cb.use_count() << endl;
int* arr = new int(100);
for (int i = 0; i < 100; i++) {
arr[i] = i * 10;
}
unique_ptr<int> ui(arr);
//unique_ptr<int> ui2(arr);
cout << arr << endl;
cout << arr[10] << endl;
cout << ui << endl;
}
void deleteInt(int* p) {
delete[] p;
}
//自定义释放规则
void test16() {
//指定 default_delete 作为释放规则
shared_ptr<int> p1(new int[10], default_delete<int[]>());
//自定义释放规则
shared_ptr<int> p2(new int[10], deleteInt);
}
void test15() {
shared_ptr<int> p1;
//shared_ptr<int> p2(nullptr);
shared_ptr<int> p2 = nullptr;
shared_ptr<int> p3(new int(3));
//shared_ptr<int> p3 = new int(3); bug代码
shared_ptr<int> p4 = make_shared<int>(3);//初始化
shared_ptr<int> p5(p4);
shared_ptr<int> p6 = p5;
shared_ptr<int> p7(move(p6));
cout << "p1:" << p1 << endl;
cout << "p2:" << p2 << endl;
cout << "p3:" << p3 << endl;
cout << "p4:" << p4 << endl;
cout << "p5:" << p5 << endl;
cout << "p6:" << p6 << endl;
cout << "p7:" << p7 << endl;
}
更多推荐


所有评论(0)