C++(20/23)标准模板库编程 - 1 C++ 回顾

引言

现代C++编程最引人注目的特点或许并非其语言本身的表达性语法与语义,而是标准模板库(STL)。STL是一个包含多功能模板类与算法的庞大集合。若运用得当,STL能显著简化和提升高性能优质软件的开发流程。然而对于许多C++程序员——无论是初学者还是资深开发者——要掌握如何有效运用STL的编程结构往往令人望而生畏。

《实用C++ STL编程》作为指导性教材,将教会您如何成功应用STL的类、算法及其他编程结构。本书涵盖广泛的STL主题,包含来自C++20C++23标准的诸多新特性。在继续阅读前需要说明:试图用一本书详尽解释每个STL组件是极不现实的。本书聚焦于C++ STL的实践编程,重点传授我认为最具核心价值且值得掌握的STL类与算法的正确应用方法。书中涵盖的具体主题及大量源代码示例,旨在加速您对STL的整体理解并激发深入学习。

本书的目标读者是专业C++程序员、学生或任何希望深入了解STL的人士。为获得最大收益,您应具备C++11或更新版本的基础知识,包括命名空间、类(构造函数、析构函数、运算符、继承等)、函数重载、lambda表达式、异常处理以及模板等概念。若对"源代码"章节提及的任一C++编译器有使用经验,也将大有裨益。

内容概述 

本书的主要目标是教授C++标准模板库编程及其组件的最佳使用方法。以下是您将学习内容的简要概述:

C++复习 - 1章回顾了关键的C++编程主题,包括类、迭代器、字符串、模板和 lambda表达式,同时解释了C++20新增的三向比较运算符。(本篇)

格式化输入输出 - 第二章探讨使用流进行格式化输入输出的方法。详细介绍了如 何正确运用C++20/23新增的字符串格式化功能,包括std::format()和std::printl n()的实现。

顺序容器 - 第三章分析顺序容器std::array与std::vector。前者是C风格数组的 现代替代品,后者无疑是STL中最重要的容器。第四章则讨论其他顺序容器,包括s td::deque、std::list和std::forward_list。本章还深入讲解迭代器及C++20新增 的迭代器概念。

通用工具库 - 第五章回顾STL通用工具库中的主要类,包括std::pair和std::tupl e。同时阐述如何最佳运用其他工具类,如std::variant、std::optional、std::any和std::expected。

智能指针 - 现代C++不推荐使用原始指针。第六章说明程序员应如何在程序中采用 智能指针作为替代方案。

关联式容器 - 第七章研究关联式容器,包括std::set、std::multiset、std::map 和std::multimap。

无序关联容器 - 第八章阐释如何使用无序关联容器std::unordered_set、std::unordered_multiset、std::unordered_map和std::unordered_multimap。本章还包 含哈希函数及其与无序关联容器关系的入门知识。

容器适配器 - 许多程序需要简单堆栈和队列来完成特定处理。第九章概述容器适 配器类std::stack、std::queue和std::priority_queue。

算法 - 第十章第十一章第十二章第十三章详述多种STL算法的应用。这些章节的讨论和源代码示例既涵盖C++11的传统算法,也包含C++20/23新增的范围变体。

范围库 - 第十四十五章解析C++20范围与视图,这些特性可简化管道处理算法的编码实现。这两章同时阐述范围适配器、投影和范围工厂。

时间库 - STL时间库包含简化日期/时间计算的类。第十六章介绍该库的重要类和算法,包括时钟类、时间点、持续时间以及日期/时间格式化功能。

文件系统 - 第17章阐述了重要的文件系统类,包括std::filesystem::path(标准库文件系统路径类)、std::filesystem::recursive_directory_iterator(递归目录迭代器)以及std::filesystem::directory_entry(目录条目类)。本章同时详解了如何运用STL提供的目录及文件创建、复制与删除算法。


数值处理 - 第18章第19章聚焦STL数值处理功能,涵盖复数运算和随机数生成器/分布器。这两章还阐释了如何运用std::valarray(值数组类)和std::slice(切片类),这些类可用于实现基于数值数组或矩阵的计算算法。


并发编程 -第20章第21章探讨了STL并发支持库的核心特性。这两章详细介绍了助力多线程应用开发的类与算法,内容涵盖STL算法执行策略、线程类、原子操作、互斥锁与信号量、条件变量,以及promise/future(承诺/未来)类。

C++回顾

如引言所述,本书内容假定读者已对C++语法基础和基本编程特性(包括类、继承、重载和模板)有一定了解。本章将回顾必须掌握的重要C++编程概念,这些概念是充分发挥STL功能的基础。同时还将阐释C++20/23中的若干新特性,涵盖主题包括:

  • 模板
  • 容器与迭代器
  • 字符串
  • 用户自定义类
  • 用户自定义模板类
  • Lambda表达式
  • 三路比较运算符
  • 异常处理

若您对某个特定主题已有足够理解,可自由选择略读或跳过该部分。反之,若对某主题理解不足,附录B列有可供参考的C++文献清单以获取更多信息。

模板

C++模板是一种参数化数据类型。程序员使用模板以类型无关的方式定义函数、类和算法。例如,假设您想编写一个新的排序算法,并希望该算法能适用于多种数据类型,如整数、浮点值、字符串和用户自定义数据类型。在许多其他编程语言中,即使每个实现执行相同的基本操作(如比较、赋值、交换等),您也需要为每种所需类型编写"不同"的算法。而使用C++模板则只需编写单一算法,只要所使用的数据类型支持所需操作即可。

让我们来看几个模板示例。清单1-1-1展示了源代码示例Ch01_01的头文件。本书中每个源代码示例都包含一个特定于示例的头文件。对于源代码示例Ch01_01,头文件Ch01_01.h包含一个单独的函数声明。

//-------------------------------------------------------------------------
// Ch01_01.h
//-------------------------------------------------------------------------
#ifndef CH01_01_H_
#define CH01_01_H_
#include "Common.h"
extern void Ch01_01_ex();
#endif

清单1-1-1中另一个值得注意的项目是语句#include "Common.h"。该文件包含模板代码,便于在使用支持C++20格式化函数(如std::format()、std::vformat()等)的编译器时,调用C++23函数std::print()和std::println()。您可以在名为Common的子目录中找到Common.h文件以及其他共享文件。就当前示例而言,无需理解Common.h中包含的模板代码。


清单1-1-2展示了文件Ch01_01.cpp的源代码。与前面描述的头文件类似,本书提供的每个源代码示例都包含类似文件。文件Ch01_01.cpp的主要用途是提供顶层异常处理程序。

//-------------------------------------------------------------------------
// Ch01_01.cpp
//-------------------------------------------------------------------------
#include <stdexcept>
#include "Ch01_01.h"

int main(int, char **argv) {
    int rc{};
    try {
        std::println("\n----- Results for example Ch01_01 -----");
        Ch01_01_ex();
    } catch (const std::exception &ex) {
        rc = 1;
        std::println("Exception occurred in program {:s}", argv[0]);
        std::println("{:s}", ex.what());
    }
    return rc;
}

示例Ch01_01相关的STL代码位于文件Ch01_01_ex.cpp中,如清单1-1-3所示。

//-------------------------------------------------------------------------
// Ch01_01_ex.cpp
//-------------------------------------------------------------------------
#include <string>
#include "Ch01_01.h"
#include "Point2D.h"
// function template - adds values
template <typename T> T add_values(T a, T b, T c)
{
    return a + b + c;
}
void Ch01_01_ex1()
{
    // uniform initialization
    int a {10};
    int b {20};
    int c {30};
    // int x = 2.1;
    // int y {2.1};
    // 使用模板函数 add_values()
    int sum1 = add_values(a, b, c);
    std::println("a: {}b: {}c: {}sum1: {}", a, b, c, sum1);
    // using template function add_values()
    double d {100.0};
    double e {200.0};
    double f {300.0};
    double sum2 = add_values(d, e, f);
    std::println("d: {}e: {}f: {}sum2: {}", d, e, f, sum2);
    // add_values() 适用于任何定义了 operator+ 的类std::string s1 {"one "};
    std::string s2 {"two "};

    std::string s3 {"three"};
    std::string s4 = add_values(s1, s2, s3);
    std::print("s1: \"{}\" s2: \"{}\" s3: \"{}\"", s1, s2, s3);
    std::println("- s4: \"{}\"", s4);
}

// 缩写函数模板 - 计算平均值
double calc_mean(auto a, auto b, auto c)
{
    return (a + b + c) / 3.0;
}
void Ch01_01_ex2()
{
    // using calc_mean()- same data types
    int a {12};
    int b {28};
    int c {36};
    double mean1 = calc_mean(a, b, c);
    std::println("a: {}b: {}c: {}mean1: {}", a, b, c, mean1);
    // using calc_mean()- different data types
    float x {201.1f};
    long long y {108};
    unsigned short z {307};
    double mean2 = calc_mean(x, y, z);
    std::println("x: {}y: {}z: {}mean3: {}", x, y, z, mean2);
    // calc_mean()- operator+ must be defined for all argument types //auto mean3 = calc_mean(a, b, "thirty"); // compiler error
}
void Ch01_01_ex3()
{
    // using template function add_values() with type Point2D<int>
    Point2D<int> p1 {10, 20};
    Point2D<int> p2 {30, 40};
    Point2D<int> p3 {50, 60};

    Point2D<int> p4 = add_values(p1, p2, p3);
    std::println("p1: {}p2: {}p3: {}p4: {}", p1, p2, p3, p4);
    // using Point2D equality operators
    std::println("p1 == p2: {}", p1 == p2);
    std::println("p1 != p2: {}", p1 != p2);
    // using Point2D accessors & mutators
    int x = p1.X() * 10;
    int y = p1.Y() * 20;
    std::println("x: {}, y: {}", x, y);
    p1.X() -= 1;
    p1.Y() -= 2;
    std::println("p1: {}", p1);
    // using Point2D::distance()
    std::println("p1.distance(p2): {}", p1.distance());
    // using template function add_values() with type Point2D<double>
    Point2D<double> p5 {100.0, 200.0};
    Point2D<double> p6 {300.0, 400.0};
    Point2D<double> p7 {500.0, 600.0};
    Point2D<double> p8 = add_values(p5, p6, p7);
    std::println("p5: {}p6: {}p7: {}p8: {}", p5, p6, p7, p8);
}
void Ch01_01_ex()
{
    std::println("\n---- Ch01_01_ex1() -----");
    Ch01_01_ex1();
    std::println("\n---- Ch01_01_ex2() -----");
    Ch01_01_ex2();
    std::println("\n---- Ch01_01_ex3() -----");
    Ch01_01_ex3();
}

文件Ch01_01_ex.cpp以多个#include语句开头。第一条语句#include <string>便于使用std::string类。需要说明的是,std::string类是STL模板类std::basic_string<char >的别名。下一条语句#include "Ch01_01.h"引入了之前描述的示例专用头文件。最后的头文件"Point2D.h"包含了一个名为Point2D的模板类定义,具体内容稍后详述。


在#include语句之后,定义了一个名为add_values()的模板函数。该函数返回三个参数值的总和。语句template

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akluse

失业老程序员求打赏,求买包子钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值