C++进阶学习

模板编程

模板函数和模板类的基本概念和用法

模板编程是C++中一种强大的特性,它允许程序员编写与类型无关的代码。这意味着你可以编写一个函数或类,让它能够处理任何数据类型。这不仅可以提高代码的重用性,还可以提高编程效率和程序的可维护性。

模板函数

模板函数是一种函数,其操作不仅适用于一种数据类型,而是可以适用于多种数据类型。你可以定义一个模板,然后在需要的时候对其进行具体化,以适应特定的数据类型。

基本语法:

template <typename T>
T functionName(T parameter1, T parameter2) {// 函数体
}
  • template <typename T> 告诉编译器以下是一个模板,T 是一个占位符类型,你可以在函数体中像使用实际的数据类型一样使用它。
  • T functionName(T parameter1, T parameter2) 定义了一个函数,其参数和返回类型由 T 指定。

示例:

#include <iostream>template <typename T>
T max(T x, T y) {return (x > y) ? x : y;
}int main() {std::cout << max(10, 5) << std::endl; // 输出: 10std::cout << max(2.3, 3.2) << std::endl; // 输出: 3.2std::cout << max('a', 'b') << std::endl; // 输出: breturn 0;
}

模板类

模板类与模板函数类似,允许你定义一个可以适用于任何类型的类。模板类非常适用于容器类,如列表、队列、栈等,因为它们的实现对于存储的元素类型几乎没有要求。

基本语法:

template <typename T>
class ClassName {
public:T memberFunction(T parameter);// 类成员
};
  • template <typename T> 同样声明这是一个模板。
  • T memberFunction(T parameter) 是一个成员函数,其参数和返回类型由 T 定义。

示例:

#include <iostream>template <typename T>
class Box {
private:T content;
public:void setContent(T newContent) {content = newContent;}T getContent() {return content;}
};int main() {Box<int> intBox;intBox.setContent(123);std::cout << intBox.getContent() << std::endl; // 输出: 123Box<std::string> stringBox;stringBox.setContent("Hello, Templates!");std::cout << stringBox.getContent() << std::endl; // 输出: Hello, Templates!return 0;
}

模板的高级用法

除了基本用法外,模板编程还有很多高级特性,如模板特化、模板的偏特化、可变参数模板等。这些高级特性使得模板编程更加强大,但同时也增加了学习的复杂度。掌握了模板的基础之后,你可以逐步深入学习这些高级话题,以充分利用C++模板编程的强大能力。

模板特化和偏特化

模板特化和偏特化是C++模板编程中的两个高级概念,它们提供了针对特定类型或类型组合进行优化或特殊处理的能力。这两个概念有助于提高程序的灵活性和效率,特别是在处理一些特定类型时需要不同于通用模板的逻辑。

模板特化

模板特化是指为模板定义一个特殊版本,这个版本仅适用于特定的数据类型或数据类型组合。当编译器遇到与特化匹配的模板使用时,它将使用特化版本而不是通用模板。

模板特化分为两类:函数模板特化类模板特化

函数模板特化

函数模板特化允许你为特定类型提供一个特殊的函数实现。

示例:

template <typename T>
void print(T value) {std::cout << value << std::endl;
}// 特化版本
template <>
void print<char>(char value) {std::cout << "Char: " << value << std::endl;
}

在这个例子中,当你使用print函数打印一个char类型的值时,编译器会选择特化版本,从而输出一个不同的格式。

类模板特化

类模板特化让你为特定类型提供一个特殊的类定义。

示例:

template <typename T>
class Box {
public:void display() {std::cout << "Generic Box" << std::endl;}
};// 类模板特化
template <>
class Box<int> {
public:void display() {std::cout << "Int Box" << std::endl;}
};

在这个例子中,当你使用Box<int>时,编译器会使用特化的类定义,从而输出一个特定的消息。

模板偏特化

模板偏特化是介于通用模板和完全特化之间的一种形式,它允许你为模板的一个子集提供特殊实现。偏特化可以应用于类模板,但不能直接应用于函数模板(函数模板可以通过重载实现类似效果)。

模板偏特化通常用于处理特定类型的组合或特定的模板参数特性,比如指针类型、一定长度的数组等。

示例:

template <typename T, typename U>
class Map {
public:void insert(T key, U value) {// 通用实现}
};// 模板偏特化:当键和值类型相同时
template <typename T>
class Map<T, T> {
public:void insert(T key, T value) {// 特殊实现}
};// 模板偏特化:处理指针类型
template <typename T>
class Map<T*, T*> {
public:void insert(T* key, T* value) {// 特殊实现,针对指针}
};

通过这些特化和偏特化的应用,你可以针对特定类型或条件提供最优或特殊的实现,这在开发通用库和组件时非常有用,可以极大提高代码的可重用性和效率。

现代 C++ 特性

C++11、C++14、C++17 甚至 C++20 的新特性

C++的现代化进程自C++11开始加速,随后C++14、C++17和C++20等版本陆续引入了许多新特性,极大地提高了编程效率和代码的可读性、安全性。下面,我会概述一些关键的现代C++特性,这些特性涵盖了从C++11到C++20的多个版本。

C++11

自动类型推断(auto)

C++11引入了auto关键字,可以自动推断变量的类型,简化了代码的编写。

auto x = 5; // x 被推断为 int
auto y = 1.5; // y 被推断为 double

基于范围的for循环

允许直接在容器上迭代,无需手动操作迭代器或计数变量。

std::vector<int> v = {1, 2, 3, 4, 5};
for(auto i : v) {std::cout << i << std::endl;
}

智能指针(Smart Pointers)

管理动态分配的内存,避免内存泄露。主要包括std::unique_ptrstd::shared_ptrstd::weak_ptr

std::unique_ptr<int> ptr(new int(10));
std::shared_ptr<int> ptr2 = std::make_shared<int>(20);

Lambda表达式

提供了一种定义匿名函数对象的方式,便于在算法中直接定义函数逻辑。

auto sum = [](int a, int b) { return a + b; };
std::cout << sum(5, 3); // 输出: 8

C++14

泛型Lambda

C++14允许Lambda表达式使用auto类型的参数,使得Lambda表达式可以更加通用。

auto lambda = [](auto x, auto y) { return x + y; };

返回类型推断

C++14扩展了函数返回类型的自动推断能力,使得函数可以根据return语句自动推断返回类型。

auto add(int a, int b) {return a + b;
}

C++17

结构化绑定(Structured Bindings)

允许从数组或tuple中直接解包值到变量中。

std::pair<int, double> p = {2, 2.5};
auto [i, d] = p; // i = 2, d = 2.5

内联变量(Inline Variables)

对于头文件中定义的全局变量,可以使用inline避免多重定义的问题。

inline int myVar = 42;

std::optional

提供了一种表达可选值(可能没有值)的方式。

std::optional<int> getResult() {return {}; // 没有值// return 1; // 有值
}

C++20

概念(Concepts)

概念是对模板参数的约束,用于指定模板参数必须满足的接口和语义要求。

template<typename T>
requires std::integral<T>
T add(T a, T b) {return a + b;
}

范围视图(Ranges)

引入了范围库,提供了更加简洁的方式来操作和组合容器和范围。

#include <ranges>
std::vector<int> v = {0, 1, 2, 3, 4, 5};
auto even = v | std::views::filter([](int i){ return i % 2 == 0; });
for(int i : even) std::cout << i << " "; // 输出: 0 2 4

这些特性只是现代C++中的一小部分,但它们是提高编程效率、编写安全和可维护代码的重要工具。随着你深入学习,你会发现更多强大的特性和技巧。

C++ 并发编程

理解并发和多线程的基本概念

在现代计算机系统中,尤其是在多核处理器变得普遍的情况下,利用并发(Concurrency)和多线程(Multithreading)能够显著提高程序的性能和响应速度。理解并发和多线程的基本概念是进行有效的并行程序设计的基础。下面对这些概念进行详细讲解。

并发(Concurrency)

并发是指两个或多个事件在同一时间段内发生。在单核处理器系统中,由于处理器在任何时刻只能执行一个任务,因此并发通常通过时间分片来实现,即操作系统轮流给每个任务分配处理器时间,从宏观上看似乎是同时执行的。在多核处理器系统中,不同的任务可以被分配到不同的处理器上,从而实现真正的同时执行。

并发不仅仅是关于同时执行多个任务,它更关注任务的分解、任务间的协作和管理,以及资源的共享。

多线程(Multithreading)

多线程是并发的一种实现方式,它允许单个程序创建多个并行执行的线程。每个线程可以看作是程序执行流的一个分支,拥有自己的执行上下文(如程序计数器、寄存器集和栈),但是与同一进程内的其他线程共享进程级别的资源(如代码、数据和文件等)。

多线程的优点包括提高程序的响应性(特别是在需要等待的操作中),更有效地利用多核处理器资源,以及简化复杂但相关任务的管理。

基本概念

  • 线程安全(Thread Safety):如果一个函数或对象在多线程环境中被多个线程同时使用时能够保持正确的行为,则该函数或对象是线程安全的。实现线程安全可能需要使用互斥锁(mutexes)、信号量(semaphores)等同步机制。

  • 死锁(Deadlock):当两个或多个线程相互等待对方释放资源而无法继续执行时,就发生了死锁。避免死锁需要仔细设计线程间的交互和资源分配策略。

  • 竞态条件(Race Condition):当多个线程访问共享资源并试图同时修改它时,程序的输出可能依赖于线程执行的顺序,这种情况被称为竞态条件。避免竞态条件通常需要通过同步机制来确保对共享资源的访问在任意时刻最多只有一个线程。

C++ 中的并发支持

C++11 标准引入了多线程支持库,提供了线程管理、同步原语(如互斥锁、条件变量等)、以及线程本地存储等功能。这些功能定义在 <thread><mutex><atomic><future> 等头文件中。

  • 创建线程:使用 std::thread 类创建新线程。
#include <iostream>
#include <thread>void threadFunction() {std::cout << "Hello, World from thread!" << std::endl;
}int main() {std::thread t(threadFunction); // 创建线程,执行 threadFunctiont.join(); // 等待线程结束return 0;
}

总结

并发和多线程是现代软件开发中不可或缺的部分,它们使得程序能够更高效地执行,更好地利用系统资源,并提高用户体验。C++ 通过标准库提供了丰富的多线程编程支持,使得实现并发程序变得更加简单和安全。然而,并发编程也引入了新的复杂性和潜在的错误源,如线程安全问题、死锁和竞态条件,因此开发者需要仔细设计和测试并发程序。

使用 C++ 的线程库(如 std::thread)

C++11 标准引入了对多线程编程的直接支持,其中 std::thread 是标准线程库的核心,提供了一种表示和管理单个线程的方式。使用 std::thread,你可以创建并行执行的任务,这对于提高应用程序的性能和响应性非常有用。下面是关于如何使用 std::thread 的基本介绍。

创建和启动线程

创建一个 std::thread 对象时,你需要提供一个函数(或函数对象、Lambda 表达式等),该函数将在新线程中执行。线程开始执行的同时,控制立即返回到创建线程的代码中,这意味着创建线程的操作是非阻塞的。

#include <iostream>
#include <thread>void helloFunction() {std::cout << "Hello from thread!" << std::endl;
}int main() {std::thread t(helloFunction); // 创建并启动线程t.join(); // 等待线程完成return 0;
}

在这个例子中,helloFunction 会在由 std::thread t 创建的新线程中执行。

等待线程完成

创建线程后,原始线程(通常是主线程)可以通过调用 join() 方法来等待新线程完成执行。join() 会阻塞调用它的线程,直到与之关联的线程结束其执行。

传递参数给线程函数

如果你想向线程函数传递参数,可以将这些参数直接放在 std::thread 构造函数中函数名之后。参数会被安全地传递给新线程中的函数。

#include <iostream>
#include <thread>
#include <string>void printMessage(const std::string &message) {std::cout << message << std::endl;
}int main() {std::string message = "Hello from another thread!";std::thread t(printMessage, message);t.join();return 0;
}

Lambda 表达式和线程

你也可以直接在 std::thread 构造函数中使用 Lambda 表达式来定义线程执行的代码。

#include <iostream>
#include <thread>int main() {std::thread t([](){std::cout << "Hello from thread using Lambda!" << std::endl;});t.join();return 0;
}

分离线程

除了 join(),还有 detach() 方法,它允许线程在后台运行,即允许线程独立于主线程继续执行。一旦线程被分离,就不能再与之进行同步(即不能再使用 join() 等待它)。

std::thread t(doSomeWork);
t.detach();

注意,分离线程需要谨慎使用,因为分离的线程在主线程结束后仍可能运行,如果主线程结束时分离线程还在访问已经被销毁的资源,则可能导致未定义行为。

总结

std::thread 提供了一种强大且相对简单的方式来创建和管理线程,使得并行编程在 C++ 中变得更加容易。理解并正确使用 std::thread 的各种功能对于编写高效、健壯的多线程 C++ 应用程序至关重要。同时,开发者需要注意线程同步和数据竞争等并发编程中的常见问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/743356.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Verilog——Verilog的历史

第1节 Verilog的历史 在传统硬件电路的设计方法中&#xff0c;当设计工程师需要设计一个新的硬件、数字电路或数字逻辑系统 时&#xff0c;需要为此设计并画出一张线路图&#xff0c;随后在CAE&#xff08;计算机辅助工程分析&#xff09;工作站上进行设计。所 设计的线路图由线…

.Net Core 与数据库

查询 Linq var indexList new long[] { 1, 2, 3}; List<long> list new List<long>(); if (String.IsNullOrWhiteSpace(request.Key) false) {var ret from aa in _db.TblAAjoin bb in _db.TblBBon aa.PId equals bb.Idjoin cc in _db.TblCCon aa.CId equals…

13、Linux-Shell02:参数传递和运算符

目录 一、参数传递 二、运算符 1、算术运算符&#xff08;、-、*、/、%、、、&#xff01;&#xff09; 2、关系运算符 3、逻辑运算符 4、字符串运算符 5、文件运算符 一、参数传递 执行脚本时可以为脚本文件传递参数&#xff0c;在脚本中可以处理这些参数。 第n个参数…

[LeetCode][LCR172]统计目标成绩的出现次数——二分找边界

题目 LCR 172. 统计目标成绩的出现次数 某班级考试成绩按非严格递增顺序记录于整数数组 scores&#xff0c;请返回目标成绩 target 的出现次数。 示例 1&#xff1a; 输入&#xff1a;scores [2, 2, 3, 4, 4, 4, 5, 6, 6, 8], target 4 输出&#xff1a;3 示例 2&#xff1a…

Elasticseach基础认识

ES的起源&#xff1f; Elasticsearch 是由 Elastic 公司创建 简称&#xff08;ES&#xff09; Elasticsearch 是一个分布式、免费和开放的搜索和分析引擎&#xff0c;适用于所有类型的数据&#xff0c;包括文本、数字、地理空间、结构化和非结构化数据。 Elasticsearch 基于 …

Oracle 主从切换脚本

一、 切换前预检查 1. dg_precheck_main_v1.4.sh #!/bin/bash#********************************************************************************** # Author: Hehuyi_In # Date: 2022年06月16日 # FileName: dg_precheck_main_v1.4.sh # # For sys user, execute the sc…

LLM之RAG实战(二十九)| 探索RAG PDF解析

对于RAG来说&#xff0c;从文档中提取信息是一种不可避免的场景&#xff0c;确保从源文件中提取出有效的内容对于提高最终输出的质量至关重要。 文件解析过程在RAG中的位置如图1所示&#xff1a; 在实际工作中&#xff0c;非结构化数据比结构化数据丰富得多。如果这些海量数据无…

【向课题组提交实习申请模板】

实习申请 尊敬的老师&#xff1a; 本人系xx学院xx专业的学生xx。现已通过xx公司202x届“xx星”实习计划的面试&#xff0c;并成功获得xx工程师实习岗位&#xff1b;工作内容为xx&#xff1b;实习地点位于xx&#xff1b;实习时长为暑期x个月。我希望能够通过此次实习&#xff0…

Redis事务及原理

Redis 事务以及原理 Redis 中的事务是一组命令的集合&#xff0c;是 Redis 的最小执行单位。它可以保证一次执行多个命令&#xff0c;每个事务是一个单独的隔离操作&#xff0c;事务中的所有命令都会序列化、按顺序地执行。服务端在执行事务的过程中&#xff0c;不会被其他客户…

HTML、CSS、JavaScript

W3C标准&#xff1a;网页主要由三部分组成 ➢结构&#xff1a;HTML ➢表现&#xff1a;CSS ➢行为&#xff1a;JavaScript HTML HTML是一门语言&#xff0c;所有的网页都是用HTML这语言编写出来的 HTML(Hyper Text Markup Language)&#xff1a;超文本标记语言 ➢超文本&#x…

【python】(10)语法糖

Python 语法糖(Syntactic Sugar)是指 Python 中的一些语法特性,它们并不改变语言的功能,但能够使代码更加简洁、易读和优雅。 1. 列表推导式(List Comprehensions) 列表推导式是 Python 中一种简洁的创建列表的方法,它允许我们使用单行代码来生成列表,避免了传统的循…

微服务篇-A JavaEE架构演进历程(学习总结)

原创作者&#xff1a;田超凡 版权所有&#xff0c;转载请注明原作者&#xff0c;严禁复制转载 Level1 传统架构 就是大家众所周知的SSM或SSH了&#xff0c; 优点&#xff1a;三层架构职责清晰 缺点&#xff1a;依赖库管理难度大&#xff0c;协同开发代码冲突和功能扩展性差&a…

如何使用vue定义组件之——子组件调用父组件数据

1.定义父子模板template <div class"container"><my-father></my-father><my-father></my-father><my-father></my-father><!-- 此处无法调用子组件&#xff0c;子组件必须依赖于父组件进行展示 --><!-- <my-…

Java Web实战(五)Web后端之AOP-面向切面编程原理用法详解

文章目录 1. 场景示例Spring AOP快速入门&#xff1a;统计各个业务层方法执行耗时 2. AOP核心概念2.1 通知类型2.2 PointCut2.3 通知顺序2.4 使用通知函数的参数2.5 示例 3. 切入点表达式3.1 切入点表达式-execution3.2 切入点表达式-annotation 4. 案例 AOP&#xff1a;Aspect…

数学实验_Matlab使用2_简单绘图

简单使用 x -pi * 2 : .1 : pi*2;y sin(x);plot(x, y); % 绘制普通图像plot(x, y, k-.*); % 绘制2维图像&#xff0c;线为实线&#xff0c;*为每个点&#xff08;Matlab的画图比较原始&#xff0c;就是简单的秒点画图&#xff09;grid on; % 打开网状格式% grid off; % 关闭…

【自动驾驶中的BEV算法】

自动驾驶中的BEV算法 在自动驾驶领域中&#xff0c;Bird’s Eye View (BEV) 算法是一种将来自不同传感器&#xff08;如摄像头、激光雷达、毫米波雷达等&#xff09;的数据转换为车辆正上方俯瞰视角下的统一表示的方法。这种转换使得车辆能够获得一个直观且具有空间一致性的环…

6. git 指定某一个提交

在 Git 中&#xff0c;直接“更新”一个特定的提交记录是不常见的操作&#xff0c;因为提交记录是 Git 仓库历史的一部分&#xff0c;通常不应该被修改。 但是&#xff0c;你可以通过一些策略来达到类似的效果&#xff0c;比如创建一个新的提交来撤销或者修改之前的提交。 以下…

SORA和大语言模型的区别

OpenAI的文生视频模型SORA与大语言模型&#xff08;LLM&#xff09;的主要区别在于它们的应用领域和处理的数据类型&#xff0c;数据处理能力、技术架构、多模态能力和创新点。SORA作为一款专注于视频生成的模型&#xff0c;展现了在处理视觉数据方面的独特优势和创新能力。 1…

R语言读取大型NetCDF文件

失踪人口回归&#xff0c;本篇来介绍下R语言读取大型NetCDF文件的一些实践。 1 NetCDF数据简介 先给一段Wiki上关于NetCDF的定义。 NetCDF (Network Common Data Form) is a set of software libraries and self-describing, machine-independent data formats that support…

GlobalExceptionHandler全局异常处理器的设计

在Java Web开发中&#xff0c;全局异常处理器&#xff08;GlobalExceptionHandler&#xff09;是一个非常重要的概念。它允许我们集中处理应用程序中可能发生的各种异常&#xff0c;从而提供统一的错误响应&#xff0c;增强用户体验&#xff0c;并简化异常处理逻辑。下面将详细…