C++学习
文章平均质量分 73
我学过,我记录。我理解,我创新。
鬼多不菜
这个作者很懒,什么都没留下…
展开
-
原子操作std::atomic
std::atomic是 C++11 标准库提供的一个模板类,用于实现原子操作。原子操作是指不会被线程调度机制打断的操作,即这种操作一旦开始,就一直运行到结束,中间不会有任何线程切换。在多线程编程中,原子操作对于确保数据的一致性和避免数据竞争至关重要。实际上,使用编译器编译的简单语句,即使仅为简单的输入输出,也是分步执行的,都不是原子操作。因此针对同一对象的访问就有可能产生不可预料的问题。在单线程的程序中,标准库提供的操作通常已经解决了这些问题。原创 2024-05-13 23:58:51 · 751 阅读 · 84 评论 -
std::async、std::future
std::promise通常与std::future一起使用,std::promise在一条线程中获得获得数据,而与之相关联的std::future(promise也有get_future()成员函数)可以在另一个线程中获得这些数据。std::launch::deferred:延迟调用,使用该参数,可以将线程入口函数调用的时间延迟到调用使用std::future的get(),wait()执行前。std::launch::async:在调用async时就调用线程入口函数,并且创建新的线程。原创 2024-05-11 18:21:38 · 384 阅读 · 26 评论 -
条件变量详解
条件变量是限制线程执行的一种机制,即在某些条件满足之前限制线程的执行。这种限制的目的,大多是为了减少线程之间信息闭塞,达到线程同步,从而提高线程之间的通信效率。线程中访问共享数据的代码常常带有一定的逻辑顺序(如数据的添加和删除)。而条件变量的目的就是将这些线程之间的代码的执行与逻辑顺序相吻合,从而避免线程不断的if判断是否达到执行要求。举个贴近的生活,你和你的好兄弟一起请求班上的好学生帮你们写作业,你好兄弟的作业先写,然后再写你的,着急的你一直询问好学生是不是轮到你了,这样会不会让好学生很烦!!!原创 2024-05-10 12:14:58 · 359 阅读 · 27 评论 -
单例类设计模式、call_once
std::call_once需要一个 std::once_flag(实际上是一个结构) 对象作为参数,该对象用于跟踪函数是否已经执行过。如果函数尚未执行,std::call_once就会执行提供的可调用对象(通常是一个函数或 lambda 表达式),并将 std::once_flag设置为已执行状态。如果函数已经执行过,std::call_once`则不会再次执行它。当第一次调用该该静态函数时,就会创建该类的唯一实例对象,当此后再调用该静态函数时,则返回已经创建的实例对象,不再创建新的实例对象。原创 2024-05-10 12:13:50 · 375 阅读 · 7 评论 -
智能指针三剑客:weak_ptr
weak_ptr并不是独立的智能指针,weak_ptr主要被用来辅助share_ptr工作。由于weak_ptr的引用计数并不与指向对象的生命周期相关联,weak_ptr可以安全的访问一个由shared_ptr所管理的对象并不改变指向对象的内容。所以使用weak_ptr访问对象不需要担心意外延长其生命周期。除此之外,weak_ptr一般还被用于解决shared_ptr产生的循环问题。原创 2024-05-06 11:23:49 · 409 阅读 · 4 评论 -
多线程、mutex互斥量
我们可以将std::lock()与std::lock_guand结合使用,先使用std::lock()避免死锁问题,再用std::lock_guard()对象接管std::lock()的锁定状态。线程可以使用lock()函数获取锁,操作完成后再使用unlock()释放锁,这样其他等待锁的线程就可以使用lock(获取锁了,但只有一条线程会获取成功,其他线程将继续等待。这些不受限制的共享数据,实际上是有一定的风险的。如果保护的范围过窄,有些对共享数据的访问未被包含,就不足以保护共享的数据,产生一系列的问题。原创 2024-05-09 13:40:16 · 978 阅读 · 19 评论 -
std::unique_lock详解
std::unique_lock的作用与std::lock_guard基本相同,也是用于管理对互斥量的锁定与解锁,但更加的灵活。std::defer_lock的作用是延迟锁的获取,表示unique_lock初始时不要尝试获取锁,而是当调用lock或者try_lock成员函数时再获取锁。当使用了std::try_to_lock后,如果没有获得锁,std::unique_lock并不会阻塞当前的线程,可以去执行一些其他的操作,等一段时间后再尝试获取锁,或者返回通知我们获取锁失败,然后继续执行代码。原创 2024-05-09 13:39:01 · 932 阅读 · 3 评论 -
线程传参、线程ID
这种参数的传递,实际上是在新线程中复制一份相同的数据,即使参数设置为了引用,但实际上仅是复制了内容,主线程引用对象的销毁并不会对新线程产生影响。由于线程函数通常接受参数的复制版本(除非特别指定为引用),因此直接传递对象会导致线程函数操作的是对象的副本,而不是原始对象。传递智能指针智能指针作线程参数时,如果传递的是unique_ptr,那么应该使用移动语义std::move(),移动后,原始的unique_ptr对象将变为空指针,线程函数将拥有该指针的所有权。线程ID通常是一个整数,用于标识不同的线程。原创 2024-05-08 14:29:40 · 352 阅读 · 23 评论 -
线程创建、join、detach
为了保证新线程执行情况,还要使用thread成员中的join或者detach确保新线程执行的完全。join的作用是阻塞主线程的执行直到新线程执行完毕,就是让主线程等一等新线程。detach的作用是将线程从进程中分离出,转而在后台运行,这样即使主线程运行完毕,新线程也不会停止运行。进程的生命周期是和主线程相同的。所以,对于自主创建的线程要确保其在主线程结束执行前执行完毕或者保持主线程,否则就可能达不到预期的效果。如果类对象的成员中包含主线程中的变量的引用、指针等,就不要使用detach确保线程的执行。原创 2024-05-08 14:28:18 · 299 阅读 · 3 评论 -
并发与线程、进程基本概念
在单核CPU时期,CPU通过交替执行任务,快速的在多个任务之间切换,使得在短时间内每个任务都得到一定的执行时间,达到了多个任务一起执行的效果。但是这种切换是有一定的开销的,当从一个任务切换到另一个任务时,需要对本任务的数据,执行状况等进行保存,以便下次切换到本任务的时候能够恢复任务的执行状况,并继续执行。而线程只是一个进程中的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。原创 2024-05-07 13:28:24 · 643 阅读 · 13 评论 -
智能指针三剑客:unique_ptr
unique_ptr是一种独占式的智能指针,有着指向对象的专属所有权。相同时间段内,一个对象只能被一个shared_ptr对象指向。当unique_ptr离开作用域或者被重新赋值时,会自动删除所指向的对象,避免内存的泄露。unique_ptr可以移动,但不可以复制。不允许两个unique_ptr指向同一个对象。和shared_ptr一样,unique_ptr如果定义时不初始化,默认为空。原创 2024-05-07 13:27:20 · 879 阅读 · 6 评论 -
智能指针三剑客:shared_ptr的使用
shared_ptr与weak_ptr对象占用的内存都是裸指针的两倍。当我们用shared_ptr对象生成weak_ptr对象时,weak_ptr对象中的指针就会指向shared_ptr指向的对象,另一个指向该 shared_ptr对象指向的控制块,就是说,指向同一个对象的shared_ptr对象和weak_ptr对象指向同一个控制块。这也说明了weak_ptr的生成要依赖shared_ptr对象的原因,因为weak_ptr对象并不能自主生成控制块,必须提供shared_ptr对象给出供它指向的控制块。原创 2024-05-06 11:24:44 · 328 阅读 · 2 评论 -
智能指针三剑客:shared_ptr
在c++中,进行动态内存的分配通常使用new和delete操作符。我们在学习这一对运算符时,一定都听过要注意在使用完new分配到内存后,一定要使用delete手动释放内存,否则就会造成内存的泄露。智能指针就是用来避免这种情况的,智能指针实际上是一个类,它的使用类似于常规指针,但增加了自动内存管理的功能。它会自动删除其所指向的对象,防止内存泄露。而且,当在异常处理中使用动态分配到内存时,智能指针可以确保内存得到正确释放,即使在异常抛出时也是如此。原创 2024-04-14 23:20:05 · 1211 阅读 · 16 评论 -
可变参模板
可变参模板是C++11引入的一个功能强大的特性。英文名是Variadic Templates。其允许我们定义参数数量可变的模板函数和模板类,更加的提高了编写的灵活性和通用性。在可变参数模板中,参数的数量在编译时才会确定。这种机制多用于元组操作、函数封装、递归模板等需要处理不确定参数数量的场景。原创 2024-04-11 23:48:52 · 184 阅读 · 12 评论 -
模板的全特化和局部特化
模板的特化是一种在编译时根据特定类型提供特定实现的技术。与模板泛化相对,其帮助我们对有一定特殊性的类型进行特殊性的处理,支持了模板泛化处理中的偏差,从而提高了代码的灵活性和可重用性。特化可以分为全特化和局部特化(也叫偏特化)。一般我们将特化版本与泛化版本的代码放在同一个.h文件中。原创 2024-04-09 21:57:27 · 912 阅读 · 12 评论 -
成员函数模板、模板的显式实例化和声明
为了防止在多个.cpp文件中相同用法使用模板类从而造成其生成多个可执行文件中出现过多的重复实例化的类模板,我们可以在其中一个.cpp文件的开头进行 实例化的定义 ,再在其他同样需要使用同类型实例化的.cpp文件中使用extern声明外部的.cpp的实例化的定义。除了虚函数,虚函数不可以成为模板函数,因为虚函数需要在子类中重新定义,但是因为没有具体的参数类型,无法生成其对应的代码,所以虚函数并不能同时为模板函数。类模板成员函数包括普通成员函数和成员函数模板,只有被我们调用了才会进行实例化,生成实例化版本。原创 2024-04-08 11:20:55 · 193 阅读 · 12 评论 -
函数/类模板详解
泛型编程是一种编程思想,其追求摆脱数据类型的束缚,通过将数据类型作为参数的处理,提高代码的通用性。模板是泛型编程的基础,它支持将数据类型作为参数,从而使得我们在处理具有相似性的对不同数据类型的处理时大大减少类代码量。模板在定义时并不会占有内存,而是在我们使用模板时针对我们的参数具体产生一个处理此类参数的函数/类,并在此时分配内存,这个过程就叫做模板的实例化。模板大致可以分为函数模板和类模板。两者有很多相似之处,但也略有不同。原创 2024-04-08 11:20:00 · 951 阅读 · 6 评论 -
左右值及其引用详解
左值表达式的特点是有持久性,可以取地址,可以被我们赋值,也就是说具有可以被我们访问的存储单元,例如变量、对象成员等。对于左值引用和右值引用,我的理解是,左值引用是内存地址和内存内容的双重绑定,而右值引用更像简单的绑定内容,或者更干脆的将临时对象起个名字,将其变为一片持久的内存地址(这种给人一种二次利用的感觉,就好像是房东将即将到期出租房改为商品房把房产证转让了)。在C++11引入了右值引用的概念之后,我们可以将引用分为三种了,也就是左值引用和右值引用,以及常量引用(其实也是左值引用的一种)。原创 2024-03-15 01:29:56 · 884 阅读 · 1 评论 -
基类与派生类简记
本文为基类与派生类的各种关系的讨论,或者直接说我想记录一下的知识点。内容有些琐碎。派生类对象包含多个组成部分,一部分是继承于基类的成员,一部分是自己定义的成员,这两部分在内存也不一定是连续存储的。这样可以反映出一个基类指针可以指向派生类对象的原因,因为我们可以把派生类对象的基类部分当成是一个基类对象用。其实,当我们这样使用的时候,编译器进行了隐式的类型转换,也就是把派生类的基类部分隐式转换为基类对象,因此我们可以把派生类当做基类来用。原创 2024-03-14 00:37:47 · 423 阅读 · 1 评论 -
RTTI详解
RTTI就是Run Time Identification,翻译过来就是运行时类型识别。它提供了一种编译器在运行时通过类型信息使用基类的指针或者引用来检查其所指向对象的实际派生类型的方法。我们已经知道,一个基类指针可以指向基类对象和个个子类对象,那我们如何直到其实际指向的对象的类型呢,RTTI就给我们提供了这种功能。RTTI主要通过dynamic_cast运算符和typeid运算符来实现这个功能的。原创 2024-03-13 17:24:37 · 1185 阅读 · 1 评论 -
友元函数、友元类、友元成员函数
friend给我们提供一种访问类成员的方式,增加了代码的灵活性。但与此同时也破坏了类的封装性,降低了类的可靠性和可维护性。原创 2024-03-13 00:32:37 · 395 阅读 · 0 评论 -
继承/派生详解
继承是C++的重要特性之一。我们对原有的类进行功能上各有侧重的扩充,可以形成多种多样的新类。这样的新类叫作原本类的派生类、子类,而原本类也相对叫作基类,父类。提示:以下是本篇文章正文内容,下面案例可供参考提示:这里对文章进行总结:继承是面向对象程序设计的核心思想之一,我们应重视其与相关知识的结合,帮助我们更好的学习C++。原创 2024-03-11 23:18:45 · 564 阅读 · 1 评论 -
多态性,虚函数详解
C++的多态性和虚函数密切相关。多态性可以说就是对虚函数而说的,就体现在具有继承关系的父类和子类之间,子类重写父类的成员函数,和虚函数动态绑定,根据实际指向类型在运行时确定调用哪个函数之中。原创 2024-03-12 22:22:23 · 1127 阅读 · 1 评论 -
类,构造、析构、运算符重载
类和对象是C++面向对象程序设计的重要概念,是C++的特色和精华。精确的理解类的使用,对学习C++具有重要意义。提示:以下是本篇文章正文内容,下面案例可供参考一类简单来讲就是我们自己设计的数据类型。和C语言的结构体非常相似。具有继承性,封装性,多态性的特点。类具体化的结果就是对象,我们通过对象将类具体化,来帮我们解决具体的问题。类可以包含多种的成员变量和成员函数。它们有的供外界使用,有的仅在本类中处理数据,不供外界使用。因此我们在类的定义中使用不同的权限修饰符来表明这些成员的访问级别。原创 2024-03-09 22:24:56 · 1085 阅读 · 0 评论 -
迭代器用法详解
迭代器实际上是一种用来遍历容器元素的数据类型(作用类似与C语言的指针)。从此就可以看出,迭代器与容器的密切联系。我们知道,一些容器(如vector)可以通过[ ]来访问容器元素,但这种方式并不普遍,因为大多数的容器并不是数组实现的,也就不能用[]来访问容器元素。因此C++中就需要一种更加普遍的工具来提高效率,这就是迭代器。迭代器是一种数据类型,一种变量。它也有几种类型,输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机迭代器。所以它支持++,--,>,=,原创 2024-02-29 14:09:02 · 1837 阅读 · 0 评论 -
string类详解
string类是C++标准库中的一个类,代表一个可变长字符串,功能也是对其的处理。与C语言中的char类型作用相似,但功能更加强大,使用也更加便捷,在c++环境中,更加适用。提示:以下是本篇文章正文内容,下面案例可供参考string s1;int a=9;定义string对象时,若未初始化对象,则对象保存内容为空。第二种和第三种都是将"hellow world"保存到内存中,但结尾中不包含/0。它们的作用一样,但建议用()的形式(因为使用=会带来隐式类型转换的问题)。原创 2024-02-27 22:39:10 · 452 阅读 · 0 评论 -
vector容器用法详解
vector是C++标准库中的一个容器。使用vector,必须通过#include将其包含过来。提示:以下是本篇文章正文内容,下面案例可供参考vector可以理解成是一种集合或动态数组。我们可以把若干数据类型相同的对象储存在其中。与数组最大的区别就是vector储存在堆上,而数组储存在栈上,vector容器的长度可以动态的扩展,当我们增加长度时,编译器会释放原本的内存,找到一片更大的内存,将数据拷贝到现内存中。原创 2024-03-01 23:08:57 · 482 阅读 · 0 评论 -
四种类型转换
类型转换的允许,扩展了数据的用途和操作方式。在必要的时刻,我们可以使用,但要清晰地认识到它所带来的影响。提示:以下是本篇文章正文内容,下面案例可供参考强制类型转换能够抑制编译器报错。使用reinterpret_cast非常危险。使用const_cast也被有些人认为是设计缺陷。不建议在自己的代码中使用。原创 2024-03-03 17:13:32 · 1061 阅读 · 1 评论