首页 > 编程语言 >C++ 高级开发者需要掌握的10个特性

C++ 高级开发者需要掌握的10个特性

时间:2023-06-19 11:24:01浏览次数:50  
标签:std 10 20 C++ 了解 开发者 使用 模板

C++ 正在快速向前发展,所以想要紧跟其脚步并不是一件容易的事。

我们在之前的文章中讨论过这个问题,讨论了 C++ 的演变以及如何实现遗留 C++ 代码现代化。在这篇文章中,我们将重点介绍经验丰富的 C++ 开发人员可以跟上的高级主题列表。

我们将尝试涵盖我们认为相关的内容,而不限于特定的 C++ 用法。在我们开始之前的最后一个注意事项:高级 C++ 内容并不一定意味着新的 C++ 特性。对于有些应当给予适当关注并且从 C++98 开始我们就一直关注的高级 C++ 主题,我们也会在列表中列出其中一部分。

需要注意的是,这篇文章并不是教程,它的目的不是教东西,而是指出您应该放入所需的 C++ 技能列表并添加到您的 C++ 学习路径中的相关高级 C++ 主题。

接下来我们就开始了。

模板

       模板是 C++ 提供的最强大的工具之一,但在某些情况下,它们并没有得到应有的利用。有些公司将模板认为是基础设施团队的工具,而“常规”C++ 团队只是在使用它们。

我认为,任何团队的高级 C++ 开发人员都应该能够在任何相关的地方实现模板,以实现代码重用、获取更高效的代码和更好的 API。

您不必了解模板的所有细节(除非您确实编写了通用模板库),但您应该从掌握简单模板函数和模板类的细节开始,然后是类型和非类型模板参数的规则(您可以将模板参数先放在一边,至少在开始时)。

C++11 增加了可变参数模板,您还应该知道如何根据需要使用这些模板。请记住,诸如 emplace、make_tuple、make_unique 和 make_shared 之类的函数全部都依赖于可变参数模板,您可能需要使用可变参数模板自己实现类似的工厂方法,这并不是理论上的。

您还可以使用模板特化,这是一种可以追溯到 C++98 的技术。无论是完全特化还是部分特化,您都会发现这种技术对于特定类型或类型族实现更有效的算法来说很有用。

另一种旧的(同样基于 C++98)但是同样有用的模板技术是标签调度。本文中也介绍了如何使用 C++20 概念来代替标签调度。此外,C++17 if-constexpr 有时可以成为标签调度的相关替代品。

C++17 增加了类模板参数推导 (CTAD),它可以更轻松地将模板类对象实例化,而无需提供模板参数,例如:

std::vector v1{1, 2, 3}; // std::vector
std::tuple p1{1, "wahad", "one"s}; //std::tuple<int, const="" std::string=""></int, const char*, std::string></int, const char*, std::string>

静态多态性这一术语也很重要。当您在编译时知道某些代码是否应该使用 TCPConnection 或 UDPConnection 时,与基于虚拟函数的动态多态性相比,正确使用模板来管理不同的实现将获得更好的运行时性能

为了总结我们的模板技术列表,我们添加了 CRTP,它经常被用作静态多态性背后的工具,但不是唯一的工具(有关在基类中实现克隆方法,请参见本例)。

请注意,本文中未列出 SFINAE,因为虽然它在 C++20 之前非常相关,但现在它已被一些概念所取代,使它看起来过时了。

模板练习:

假设我们需要管理一个大多数条目 (99%) 为假的布尔数组。为“大布尔数组”和“小布尔数组”实现基于模板的策略,这样用户只需创建一个布尔数组,并根据请求的大小选择底层实现:

BoolArray<5> b1; // all values are initialized to false
// ^ the underlying internal data structure would be bool arr[5]

BoolArray<1005> b2; // all values are initialized to false
// ^ the underlying internal data structure is std::set<size_t>
//    holding only all the “true” indices (which are expected to be a few)

// following operations shall be supported:
// [1] simple assignment
b1[0] = true;
b2[0] = true;

// [2] toggle values using range-based-for
template<size_t SIZE>
void toggle_a_few(BoolArray& b) {
    for(auto&& val: b) {
        if(some_rare_case)
            val != val;
    }}

您可以点击此处查看上述练习的解决方案(不要偷看,先尝试自己解决)。

右值和移动语义

在我们进行的两项调查中(CoreCpp 2021 和 CppCon 2021),右值和移动语义在被引入 C++ 10 年后被认为是一个复杂的话题。

你应该知道什么是右值,并且知道左值和右值的重载解析。

至于移动语义和 std::move,不使用移动语义意味着放弃性能提升。而错误地使用它则意味着潜在错误。

这里有一些例子:

在需要时忘记使用 std::move :

class Pesron {
    std::string name;
public:
    Person(const std::string& p_name) : name(p_name) {}
    Person(std::string&& p_name) : name(p_name) {} // oops
    // ...
};

您能说出标有 “oops” 的那行有什么问题吗?

适当时不使用 std::move 的另一个示例:

// popping the last element from a vector
auto val = a.back(); // allow call of move ctor if available
a.pop_back();

您能告诉我们如何使上面的代码更有效吗?

好吧,请看这里:

auto val = std::move(a.back()); // allow call of move ctor if available
a.pop_back();

您应该注意的其他问题包括实现移动忘记`noexcept`的问题、在能够使用的时候不使用零规则、丢失良好的默认移动操作而不用 =default 取回它们、在不应该使用时使用 std::move(从您仍在使用的对象中窃取)、在相关时不使用 std::forward。

定位 new

定位 new 的概念是,我们可以在特定的给定内存位置创建一个对象,方法是调用该内存位置上的构造函数,而无需实际分配内存。当一个新项目被添加到一个已经分配的容量中时,std::vector 会使用这个项目。了解和理解定位 new 对于理解 std::vector 的实现方式非常重要。

强类型

使用用户定义的文字类型,现在可以轻松直接地使用像 std::chrono 这样的强类型。其主要思想是将数据与其测量单位联系起来。测量单位的错误解释是导致卫星坠毁的已知原因(您可以单击此处和此处了解有关 Ariane 5 坠毁的更多信息,也可以单击此处了解有关火星气候轨道飞行器坠毁的更多信息)。

如果您还没有用过强类型,您可能需要进一步了解。例如,可参阅 Joe Boccara 的强类型库,可点击此处了解详细内容。其他强类型库实现包括:

  • https://github.com/nholthaus/units

  • https://github.com/pierreblavy2/unit_lite

  • https://github.com/bernedom/SI

  • https://github.com/mpusz/units

智能指针

同样,根据我们进行的调查(CoreCpp 2021 和 CppCon 2021),内存泄漏和调试内存错误在 2022 年仍然是一个问题。这可以通过更好地使用 C++ 智能指针来解决。

了解 unique_ptr、shared_ptr 和 weak_ptr 很重要。正确使用智能指针将有助于使类遵循零规则。

使用智能指针实现适当的 API 对于实现系统设计目标至关重要。每种智能指针类型都有自己的语义和特定用例。Herb Sutter 有几篇关于这个主题的经典帖子,您可能想要关注,例如:GotW #89 智能指针和 GotW #91 智能指针参数。

容器和算法

无需引入 std::vector,但您可能仍希望确保避免 std::vector 陷阱。您还应该了解 C++17 新增的 std::optional、std::variant、std::any 和 std::string_view 以及何时使用它们。此外还有 C++20 新增的 std::span 和范围库。

知道 std::array 是一种聚合类型并且它不重视元素初始化也很重要(即,当创建整数的 std::array 时,整数值默认不会自动初始化为零,这与初始化为零的 std::vector 正好相反)。

问题:如何创建一个包含 100 个整数的 std::array 并将它们全部初始化为零?(注意:std::array 没有适当的构造函数,实际上它没有任何构造函数。)

答案:

std::array<int, 100> arr {}; // aggregate initialization, all ints are initialized to zero

最后,布尔向量的秘密行为虽然不是必须要掌握的东西,但是了解它会让您进入专家阵营。

至于算法,重要的是要在实现自己的算法之前检查标准算法(包括范围算法)。在许多情况下,您会发现您要实现的算法要么是现有的,要么可以使用现有的算法来实现。

Lambdas

C++11 将 Lambda 表达式添加到 C++ 中,而 C++14、C++17 和 C++20 中则添加了新的重要语法选项(请参阅关于 lambda 演变的两部分博客文章:第 1 部分和第 2 部分)。

如果您使用 C++11 或更高版本,您很可能知道 lambda 表达式。确保您也了解其中的新增内容(参见上面的链接,以及其他资源,如 Jason Turner 的 C++ Weekly 中关于 lambda 的播放列表。如果您全部观看完,您将真正掌握 lambda!)。

持续评估

C++11 添加了 constexpr 关键字,它允许定义在编译时分配有已知值的常量,以及可以在编译时执行的函数。随着 C++ 不同版本的演变,constexpr 的可能性显着发展,消除了 C++11 中 constexpr 函数的许多限制(您可以在 constexpr cppreference 页面和关于该主题的这篇非常详尽的博客文章中了解演变内容)。

了解在编译时可以做什么对性能很有用。使用 C++17 if constexpr,您可以编写更好的通用代码,避免 SFINAE 或概念中不必要的复杂重载。从 C++20 开始,std::string 和 std::vector 都具有 constexpr 构造函数,因此可以在编译时创建。

C++20 还添加了 consteval 和 constinit,您可能想要了解它们(例如,Jason Turner 的 C++ Weekly 中关于 consteval 和 constinit 的视频)。

多线程和并发

多线程和并发通常是一个很大的话题,特别是在 C++ 中。从如何使用 std::thread 的基本语法开始将是一个很好的起点。了解 C++20 std::jthread 可使您具备基础知识。

在编写并发代码时,知道如何以及在何处使用互斥锁和锁守卫当然是至关重要的。这包括用于多个互斥锁的有用 C++17 互斥锁包装器,即 scoped_lock。

在上述知识的基础上,最好再了解 C++20 新锁、counting_semaphore 和二进制信号量以及 std::latch 和 std::barrier。

了解容器的线程安全规则很重要,因为在某些时候您会很自然地在多线程应用程序中使用标准容器。

知道如何使用原子变量和诸如 compare_exchange 之类的原子操作可以让您实现无锁算法。

了解 std::promise、std::future 和 std::packaged_task 后,您能够实现更好的基于多线程的异步操作。

最后,了解如何以及何时使用 thread_local 变量也很重要。

std::conditional_variable 的使用可以留给库实现人员,但如果您处于那个位置,那么也应该了解其使用。

与本文的其他部分一样,这不是所有 C++ 并发类和实用程序的完整列表(可以点击此处进行了解)。但它应该会为覆盖大部分重要内容铺平道路。

新的 C++20 特性

我们已经在前一篇文章中介绍了 C++20 的四大方面:模块、概念、协程和范围。在上一篇博文中,我们还提到了 C++20 中添加的 spaceship 运算符。我们还专门发布了一篇有关协程的文章。您应该熟悉这些新增内容,但是如果您还没有使用 C++20,那么您当然不需要掌握它们。

总结

我们在这篇文章中尝试列出我们认为 C++ 高级开发人员应该知道的和通常使用的 C++ 主题。当然,C++ 中还有其他高级和重要的特性和主题没有涉及到。

我们跳过了我们认为基本的内容。但我们也可能会忽略可以添加到列表中的高级功能。这绝不是一个详尽的列表,几乎可以肯定的是,您可以想到其他可以添加的高级 C++ 项目。

C++ 是一门非常丰富的语言,而且还在不断发展!全部掌握它几乎是不可能的。每个 C++ 开发人员的目标应该是不断学习,持续关注新特性(同时确保您对旧特性不会有知识空白)——确保您不会落后。

标签:std,10,20,C++,了解,开发者,使用,模板
From: https://www.cnblogs.com/blizzard8204/p/17490666.html

相关文章

  • 方法对了,你做1年Android开发能顶别人做10年
    前几天后台有读者问我这样的问题。他在一家互联网公司工作3年了,每天都很忙,事情又多又杂。本想着学习多一些东西也不是坏事,可到头来一无所获,什么都没学会,满腔的热情也被消磨得差不多。三天两头动辞职的念头,但又不知道自己还能做什么,甚至开始后悔:如果当初选择另一个行业,是不是就会好......
  • win10打开Sourcetree闪退解决方法
    前言昨天Sourcetree还可以正常使用,今天早上打开后出现下图就闪退了 百度尝试各种操作后找到解决方法,不知是否通用希望有人可以留言告知:删除掉:C:\Users\Administrator\AppData\Local\Atlassian目录下的如图文件,我是装在Administrator下面的,其他人按照实际位置删除 ......
  • 介绍一个C++奇巧淫技
    你能实现这样一个函数吗:MyTypetype;HisTypehtype;serialize_3(11,type,htype);serialize_4(type,htype,type,htype);serialize_4(11,type,htype,htype);参数类型自由,个数自由,怎么做呢?往下看:[xiaochu.yh@OBmacro]$catauto_type.cpp/**(C)1999-2013......
  • 20230421 10. 模板方法模式 - 试卷答题
    既然用了继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理模板方法(Templa......
  • 10个ai算法常用库java版
    根据AI项目的具体需求,可以选择最合适的库或框架,并开始尝试使用不同的算法来构建AI解决方案。1.Deeplearning4j 它是一个用于Java和Scala的开源分布式深度学习库。Deeplearning4j支持各种深度学习架构,包括卷积神经网络(CNN)、递归神经网络(RNN)和深度信念网络(DBN......
  • Ubuntu 23.10 将引入安全增强的 PPA
    导读 Ubuntu的升级不断地增强功能并添加安全修复程序。但是,很少见到一些核心机制的更改。 在Ubuntu23.10中,PPA的功能得到了改进。至少,你在终端中看到警告会更少。这是什么意思呢?让我详细说明一下。GPG密钥问题传统上,PPA和其他外部存储库是通过 /etc/apt......
  • 《C++》多态
    多态多态分为两种:静态多态:函数重载和运算符重载属于静态多态,复用函数名动态多态:派生类和虚函数实现运行时多态静态多态函数地址早绑定--编译阶段确定函数地址动态多态函数地址晚绑定--运行阶段确定函数地址virtual  //修饰虚函数,使之变为动态多态特点代码结构清晰可读性强......
  • C++常用数据结构
    数据结构1.线性表由n个具有相同性质的数据元素1.1顺序表(数组)定义:用一组地址连续的存储单元依次存储线性表中每个数据元素特点:逻辑关系相邻的两个元素在物理位置上也相邻#c++实现template<typenameT>classsqlist{public:sqlist(intmaxsize=10):Maxsize(......
  • 现代C++学习指南-类型系统
    在前一篇,我们提供了一个方向性的指南,但是学什么,怎么学却没有详细展开。本篇将在前文的基础上,着重介绍下怎样学习C++的类型系统。写在前面在进入类型系统之前,我们应该先达成一项共识——尽可能使用C++的现代语法。众所周知,出于兼容性的考虑,C++中很多语法都是合法的。但是随着新......
  • #yyds干货盘点#C++命名空间
    命名空间命名空间是C++语言的新特性,它能够解决命名冲突问题。例如,小明定义了一个函数swap(),C++标准程序库中也存在一个swap()函数。此时,为了区分调用的是哪个swap()函数,可以通过命名空间进行标识。C++中的命名空间包括两种,具体介绍如下。usingnamespacestd;1.标准命名空间std是C+......