各位未来的自己:当你翻开这篇笔记时,大概率正对着当初写的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这种长到哭的类型。当初的坑:分不清autoconst和引用的推导规则,瞎写导致编译报错。

改名后代码(原 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 nauto f = n推导为int);
  • auto接引用时:保留 const(比如const auto& r1 = x→推导为const int&);
  • 别用auto定义数组(比如auto arr[5] = {1,2,3}会报错),auto只能推导单个变量类型。

2. decltype:变量类型的 “照妖镜”

核心作用:“照” 一下某个表达式的类型,原样抄下来(比auto更精准,保留所有修饰)。当初的坑:不知道decltypeauto的区别,该用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. 右值引用:“捡临时垃圾” 的高效引用

核心作用:绑定到临时值(右值,比如10a+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);右值:没名字、临时的(比如10a+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. 不能用于定义数组(如auto arr[5]报错);

3. 当类型为引用时,保留表达式的 const 属性

decltype 精准反射表达式的原生类型,不忽略 const、引用等修饰符

1. 完全保留表达式的所有类型修饰(如const int&推导后仍为const int&);

2. 常用于泛型函数的尾置返回类型(如auto add(T t,U u)->decltype(t+u));

3. 仅分析表达式类型,不执行表达式

右值引用 绑定到右值(临时值、字面量等),实现移动语义,避免不必要的拷贝

1. 用&&声明(如int&& ref = 10);

2. 仅能绑定右值,绑定左值需用std::move(如int x=5; int&& ref=std::move(x));

3. std::move后原左值会变成 “空壳”,避免后续使用

shared_ptr 共享堆内存所有权,通过引用计数自动释放内存,避免内存泄漏

1. 用std::make_shared初始化更安全高效(如auto sp = make_shared<int>(10));

2. 引用计数为 0 时自动释放内存;

3. 避免循环引用(需配合weak_ptr解决);

4. 不管理栈内存(如shared_ptr<int> sp(&x)会导致重复释放)

weak_ptr 作为shared_ptr的观察者,解决其循环引用问题,不拥有内存所有权

1. 不增加shared_ptr的引用计数;

2. 需通过lock()转换为shared_ptr后访问对象(如auto sp = wp.lock());

3. 用expired()判断指向的对象是否已释放;

4. 不能直接用*访问对象

unique_ptr 独占堆内存所有权,禁止拷贝,仅支持移动,轻量高效

1. 不能拷贝(如unique_ptr<int> up2 = up1报错),仅能通过std::move转移所有权;

2. 不允许多个unique_ptr管理同一内存;

3. 推荐用std::make_unique初始化(C++14 及以后支持)

Lambda 临时定义匿名函数片段,无需单独声明函数,灵活配合算法(如sortfor_each

1. 基础格式:[捕获列表](参数列表)->返回值类型{代码}(返回值可自动推导时省略->返回值类型);

2. 捕获列表:[=]值捕获(只读,改副本需加mutable)、[&]引用捕获(可修改外部变量);

3. 配合for_each时需包含<algorithm>头文件,参数类型需与遍历元素匹配;

4. 可赋值给同类型函数指针

可变参数模板 定义支持任意数量、任意类型参数的模板(函数 / 类),实现泛型灵活性

1. 用...声明参数包(如template<typename... Args> void func(Args... args));

2. 需通过递归展开(需写终止函数)或 C++17 折叠表达式(如(cout << ... << args))展开参数包;

3. 用sizeof...(Args)获取参数包大小

array 固定大小的安全数组,兼具普通数组的高效和容器的成员函数(如at()

1. 需包含<array>头文件;

2. 大小为编译期常量(如array<int, 5>,5 不能是变量);

3. 用at(索引)安全访问(越界抛异常),[]访问不检查越界;

4. 支持front()back()get<索引>()(编译期访问)等成员函数

forward_list 单向链表容器,仅支持从头部高效操作,内存开销小

1. 需包含<forward_list>头文件;

2. 无push_back成员函数(尾部操作低效),仅支持push_frontemplace_front(头部插入高效);

3. 自带sort()成员函数,不支持随机访问迭代器;

4. 遍历需用前向迭代器,不能反向遍历

unordered_set(无序去重集合) 无序存储元素,自动去重,基于哈希表实现快速插入 / 查找 / 删除(平均 O (1))

1. 需包含<unordered_set>头文件;

2. count(值)仅返回 0(元素不存在)或 1(元素存在),因自动去重;

3. 元素无序,不支持排序操作;

4. 插入重复元素时会自动忽略

unordered_multiset(无序可重复集合) 无序存储元素,允许重复,支持统计重复元素个数,哈希实现高效操作

1. 需包含<unordered_set>头文件;

2. size()统计所有元素(包含重复项);

3. count(值)返回该元素实际出现次数;

4. 元素无序,插入顺序不影响存储顺序;

5. 插入重复元素会保留所有副本

unordered_map(无序键值对容器) 无序存储键值对(键唯一,一个键对应一个值),快速通过键索引值(哈希实现)

1. 需包含<unordered_map>头文件;

2. 初始化列表中每个键值对需用{}包裹(如{"张三", 90});

3. 访问值优先用at(键)(键不存在抛异常,安全),[]会自动插入无效键(值为默认 0,易产生垃圾数据);

4. 键重复时,后插入的会覆盖前值;

5. 遍历用range-based for,每个元素是pair<键类型, 值类型>first为键,second为值)

unordered_multimap(无序可重复键键值对容器) 无序存储键值对(键可重复,一个键对应多个值),支持 “一对多” 映射

1. 需包含<unordered_map>头文件;

2. 无[]访问方式(键对应多值,无法确定返回哪个);

3. 需通过find(键)查找首个匹配键,或equal_range(键)获取所有匹配键的范围(返回迭代器对);

4. 键可重复,插入多个相同键会生成多个独立键值对;5. size()统计所有键值对个数(包含重复键)

四、结尾:以后复习别再 “猜代码” 了!

这篇笔记把当初的 “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;


}



Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐