![](https://img-blog.csdnimg.cn/20190918140158853.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
#C++进阶
文章平均质量分 92
介绍C++的比较深入的知识,比如模板、C++标准库的源码分析等等
流星雨爱编程
记录工作的日常,心得体会
展开
-
C++20之std::span:高效访问容器的神器
C++20引入了std::span作为一种语法糖,用于表示连续内存范围。它提供了一种轻量级的、非拥有式的、零开销的方式来引用数组或其他连续内存块。std::span可以用于传递数组片段给函数,或者在函数内部对连续内存进行操作,而无需进行内存拷贝。std::span提供连续对象序列的轻量级视图。 span 提供了一种安全的方法来对在内存中背靠背排列的对象进行迭代和索引。 例如存储在内置数组中的对象 std::array 或 std::vector。如果你通常使用指针和索引访问一系列背靠背对原创 2024-07-17 22:56:57 · 1188 阅读 · 30 评论 -
C++中跨平台类的设计方法
本文从类的设计角度来考虑代码的跨平台性,其实在实现的过程还需要综合考虑平台差异、标准库和第三方库的选择、运行时环境差异的处理、跨平台编译工具的使用、跨平台测试与调试以及跨平台接口设计等多个方面。通过合理的设计和实现,可以大大提高代码的可移植性和复用性,降低维护成本。原创 2024-07-14 18:35:54 · 767 阅读 · 8 评论 -
深入理解C++构造函数
在C++中,自定义构造函数允许你为类的实例提供特定的初始化逻辑。构造函数初始化列表是构造函数体之前的一个冒号()后跟一个以逗号分隔的初始化器列表。它用于初始化成员变量,特别是那些不能通过赋值来初始化的成员(如const成员、引用成员或没有默认构造函数的类类型的成员)。就属于自定义构造函数。构造函数是C++中非常重要的概念,它们用于在对象创建时初始化对象的成员变量。构造函数没有返回类型,并且其名称必须与类名相同。通过构造函数,可以确保对象在创建时处于有效的状态。原创 2024-07-14 11:22:45 · 809 阅读 · 1 评论 -
浅谈C++中的防御性编程
在实际开发中,防御性编程不仅仅是一个技术问题,更是一种编码思维和习惯的养成。以下是一些具体的实践建议:代码审查定期进行代码审查,发现潜在的错误和问题。通过集体智慧,可以提高代码的质量和健壮性。编写单元测试单元测试可以帮助验证代码的正确性,捕获边界条件和异常情况。编写全面的单元测试是防御性编程的重要组成部分。持续学习和改进防御性编程是一门需要不断学习和实践的艺术。通过阅读相关书籍、博客和参加技术讨论,可以不断提高自己的防御性编程水平。原创 2024-07-06 10:35:15 · 1631 阅读 · 6 评论 -
C++之thread_local变量
thread_local 是 C++11 为线程安全引进的变量声明符。表示对象的生命周期属于线程存储期。线程局部存储(Thread Local Storage,TLS)是一种存储期(storage duration),对象的存储是在线程开始时分配,线程结束时回收,每个线程有该对象自己的实例;如果类的成员函数内定义了 thread_local 变量,则对于同一个线程内的该类的多个对象都会共享一个变量实例,并且只会在第一次执行这个成员函数时初始化这个变量实例。原创 2024-06-21 20:14:14 · 1278 阅读 · 15 评论 -
C++之std::type_identity
std::type_identity是 C++17 引入的一个实用工具,用于确保类型别名保持其引用的完整性。在某些模板元编程的场景中,尤其是在与类型萃取(type traits)和完美转发(perfect forwarding)相关的场景中,保持类型的“原样”传递是非常重要的。 std::type_identity是一个简单的模板,它定义了一个别名type,该别名简单地重新声明了其模板参数类型。但重要的是,它不会修改或“破坏”传递给它的类型。原创 2024-06-18 22:57:15 · 1011 阅读 · 14 评论 -
C++模板之模板成员函数不能偏特化
好了,本篇小文就写到这里,总结一下内容,首先实例代码讲解了很多 C++ 书籍都提到,但是没有细说的:“虽然类模板可以偏特化,但是类模板的成员函数不可以偏特化” 这句话的意思,然后又介绍了两种利用函数重载机制实现的变相函数“偏特化”的方法。原创 2024-06-16 12:23:57 · 1071 阅读 · 9 评论 -
C++之noexcept
综上所述,C++11通过引入noexcept关键字、智能指针、移动语义等特性,以及增强STL和其他库,显著提高了C++的异常安全性。这些特性使程序员能够更好地管理资源、避免资源泄漏和未定义行为,并在异常发时代码的健壮性和可靠性。noexcept。原创 2024-06-04 23:14:16 · 821 阅读 · 10 评论 -
C++17之std::void_t
std::void_t 可以用来判断一个类型 T 是否嵌套定义了某个类型。判断的原理就是利用 T::SomeType 类型的存在性,转化成 std::void_t 定义的合法性。原创 2024-06-02 22:04:27 · 858 阅读 · 10 评论 -
C++模板为何通常定义在头文件中?
C++模板的强大之处在于它们的类型灵活性和编译时实例化。然而,这些特性也带来了一些特殊的编译和链接考虑。将模板定义放在头文件中是确保所有使用模板的编译单元都能看到完整定义的有效方法,从而避免了分离编译问题。虽然这种做法可能会增加编译时间和头文件的大小,但它确保了模板的正确性和可用性。在编写和使用模板时,了解这些编译和链接细节是至关重要的,因为它们直接影响到代码的结构、可维护性和性能。原创 2024-05-31 19:29:30 · 970 阅读 · 7 评论 -
C++之std::is_trivially_copyable(平凡可复制类型检测)
它是在标头定义主要用来判断T是否平凡可复制类型。并非非潜在重叠子对象的可平凡复制类型的对象,是仅有的能以安全复制或以序列化自/到二进制文件的 C++ 对象。在 C++11 及其之后的版本中,如果一个类型是可平凡复制的,那么你可以安全地通过memcpy或memmove等函数进行复制,而不需要担心可能的副作用(如析构函数的调用或虚函数的重新定向等)。然而,你应该注意,即使一个类型是可平凡复制的,也并不意味着你应该总是使用memcpy来进行复制;在许多情况下,使用赋值操作符或复制构造函数是更安全、更清晰的选择。原创 2024-05-26 19:23:55 · 1018 阅读 · 9 评论 -
C++三剑客之std::any(二) : 源码剖析
到此我们已经全部分析完毕,任何做技术的都是要知其然,更好知其所以然。只要这样,才能把这些设计手法运用到我们平时的项目当中,只有你能熟练的运用了才是真正的掌握了。还是那句话,纸上得来终觉浅,绝知此事要躬行。相关推荐阅读C++内存分配策略。原创 2024-05-23 23:33:06 · 1074 阅读 · 13 评论 -
史上最全C/C++面试题集锦
select、poll、epoll都是IO多路复用的一种机制,可以监视多个文件描述符,一旦某个文件描述符进入读或写就绪状态,就能够通知系统进行相应的读写操作。Select优点:可移植性好,因为在某些Unix系统中并不支持poll和epoll对于超时时间提供了更好的精度:微妙,而poll和epoll都是毫秒级Select缺点:支持监听的文件描述符fd的数量有限制,最大数量默认是1024个。原创 2024-05-20 22:58:51 · 596 阅读 · 10 评论 -
C++之std::bitset使用精讲(全)
bitset满足可复制构造 (CopyConstructible)及可复制赋值 (CopyAssignable)的要求。下面是// 创建一个长度为 N 的 bitset,所有位都被初始化为 0// 使用二进制整数 value 初始化一个长度为 N 的 bitset// 使用二进制字符串 string 初始化一个长度为 N 的 bitset//用整个字符串来初始化bitset// 使用另一个 bitset 初始化一个长度为 N 的 bitset。原创 2024-05-16 22:58:23 · 4834 阅读 · 21 评论 -
手撕代码:实现自己的unique_ptr
通过实现自定义的UniquePtr,我们不仅学习了智能指针的内部机制,还掌握了如何管理动态分配的内存资源,以及如何设计可重用和可扩展的C++代码。当然,实际生产中的智能指针实现会更加复杂,需要考虑更多的边界情况和性能优化。现在,我们的UniquePtr类已经具备了基本的智能指针功能,能够自动管理内存,并且支持移动语义和自定义删除器。这个实现虽然简单,但足以展示智能指针的核心思想和工作原理。在实际项目中,建议使用标准库提供的std::unique_ptr,因为它已经原创 2024-05-13 20:00:07 · 327 阅读 · 1 评论 -
std::ref和std::cref的使用和原理分析
std::reference_wrapper是一个模板类,用于包装引用,使其能够在容器中存储或以引用的形式传递。它提供类似引用的语法,并且可以与标准容器一起使用,因为容器无法直接存储引用。其实使用std::ref时,后台真正起作用的关键类是std::reference_wrapper,它才是真正包装引用的类。#if!原创 2024-05-12 12:49:06 · 1003 阅读 · 0 评论 -
C++中的std::bind深入剖析
std::bind在 C++11 中引入,提供了一种将可调用对象(如函数、成员函数、函数对象等)与参数进行绑定的方式。然而,随着 C++ 的发展,尤其是 C++11 之后的版本,std::bind的使用逐渐受到了一些限制,因为 lambda 表达式提供了更加灵活和直观的方式来实现类似的功能。以下是std::bind优点:灵活性std::bind可以绑定函数、成员函数、函数对象等的参数,提供了一种灵活的方式来创建新的可调用对象。与结合使用std::bind创建的可调用对象可以很容易地赋值给。原创 2024-05-09 23:31:00 · 2043 阅读 · 15 评论 -
C++之std::tuple(一) : 使用精讲(全)
C++11之后引入了std::tuple,俗称元组,元组(tuple)是一种用于组合多个不同类型的值的数据结构。元组可以将不同类型的数据打包在一起,类似于一个容器,可以按照索引顺序访问其中的元素。元组的大小在编译时确定,不支持动态添加或移除元素。std::tuple类似互C语言的结构体,不需要创建结构体而又有结构体的特征,在某些情况下可以取代结构体而使得程序更加简洁,直观。std::tuple理论上可以定义无数多个不同类型的成员变量。原创 2024-02-03 15:44:14 · 5169 阅读 · 20 评论 -
C++反射之检测struct或class是否实现指定函数
诸如Java, C#这些语言是设计的时候就有反射支持的。c++没有原生的反射支持。并且,c++提供给我们的运行时类型信息非常少,只是通过typeinfo提供了有限的一些支持。这一点点支持其实连类型名都无法打印好。更别说去检测一个结构体或类是否具有实现指定函数。通过编写模板代码和或requires(C++20)表达式,你可以根据某个类型是否拥有特定的成员函数或方法来启用或禁用某些代码。这种方法不会直接告诉你一个类型是否实现了某个函数,但它允许你根据类型的能力编写条件编译的代码。原创 2024-05-08 22:07:00 · 353 阅读 · 6 评论 -
C++实现自定义对象支持Range-based循环语法
自定义类支持range-base for需要满足的条件:1)类中需要定义容器相关的迭代器(这里的迭代器是广义的,指针也属于该范畴)2)类中要有begin()和end()的成员方法,返回值为迭代器(或重载全局的begin()和end()也可以)//返回第一个迭代子的位置//返回最后一个迭代子的下一个位置3)迭代器必须支持!=、*解引用、前置++等操作operator++(自增),可以在自增之后返回下一个迭代子的位置operator!= (判不等)operator* (解引用)原创 2024-05-08 11:33:04 · 782 阅读 · 5 评论 -
C++之内联(inline)
内联的优势其实可以分成两个部分,一个是调用方面的,比如前面说的出栈入栈等;另外一个是调用时的优化,比如函数代码如果成为内联函数,编译就可以把一些类似的固定计算直接指定为计算的结果值。而内联的劣势,主要就在于内联导致的代码膨胀,而有些时候过度内联反而会导致性能的丧失。明白了内联的优缺点,就可以根据自己的实际开发需求来进行使用了。整体上而言,内联函数适合于小型、高频函数的调用,通常可以在ORM的数据库属性读写中看到。原创 2024-05-05 18:57:03 · 893 阅读 · 2 评论 -
C/C++零长度数组的妙用
其实零长度数组是一个技巧,这样的小技巧在C/C++中有很多。它们在一些特定的场景下有着非常好的应用,不过前面的定语一定要记清楚,特定的场景。这也是C/C++让初学者感到难缠的一个重要原因。到处都有一些特殊情况,而特殊情况里可能又套着特殊情况。这种小技巧的东西,不用刻意学习,用到了,想起来有,查查资料用就可以了。原创 2024-05-05 11:44:00 · 606 阅读 · 3 评论 -
C++中的explicit关键字详解
在C++中,explicit关键字用于防止类构造函数、转换运算符或其他函数的隐式自动转换。使用explicit可以提高代码的可读性和健壮性,避免意外的类型转换导致的错误。以下是关于explicit关键字的详细介绍和示例。原创 2024-05-03 09:15:14 · 285 阅读 · 8 评论 -
C++临时对象的产生及优化
优化是一个迭代的过程,需要根据具体的代码和用例进行调整。在优化之前,最好先测量代码的性能,以便了解优化的效果。同时,也要注意不要过度优化,以免引入难以维护的代码或产生意外的副作用。原创 2024-05-01 07:23:16 · 809 阅读 · 6 评论 -
C++14之std::exchange的使用和原理分析
1) 原子性:std::exchange 提供了原子操作,这意味着在交换过程中,其他线程不会看到中间状态。这对于需要精确控制数据状态的多线程应用程序非常有用。2) 返回值:std::exchange 返回交换前的值。这允许你在一个操作中同时获取和设置值。3) 用途广泛:除了直接的数据交换,std::exchange 还可以与算法和其他模板一起使用,以提供更复杂的操作。4) 与 std::swap 的区别:std::swap 也是用于交换两个值的函数,但它不保证原子性。原创 2024-04-29 16:11:25 · 728 阅读 · 9 评论 -
手撕代码: C++实现数据的序列化和反序列化
在C++应用程序中,经常会涉及到对一些数据进行序列化和反序列化的处理。序列化可以将一个对象转换为一串字节流,这样就可以将其存储在硬盘上或者通过网络传输到其他设备上。而反序列化则是将这些字节流解析成原始的对象。在Qt中,数据的序列化和反序列化可以使用QDataStream类来完成。QDataStream是一个方便的Qt类,它可以将基本数据类型、Qt数据类型以及用户定义的数据类型都进行序列化和反序列化。在使用QDataStream进行序列化时,需要指定一个QIODevice类的子类(例如QF原创 2024-04-24 21:42:54 · 1468 阅读 · 6 评论 -
C++中不使用中间变量交换两个变量值的方式
此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。3) 在实际编程中,如果对变量的值范围有明确的了解,并且确信不会导致溢出,可以使用算术运算符进行交换。这种算法先将a和b的值赋给a,之后b等于a-b,这是b的值就变成了原来的a,最后a=a-b,a的值变为原来的b。如果a和b的值非常大,相加后可能会导致整数溢出,从而导致交换后的值不正确。交换两个数的值是比较基础也比较常用的算法,一般在交换两数的值是,最简单的方法适用的方法就是。^ : 异或运算,转成2进制,再相加。原创 2024-04-20 22:11:48 · 539 阅读 · 10 评论 -
C++进阶技巧:如何在同一对象中存储左值或右值
要定义const访问,需要使变量内部的三种可能类型中的每一种都产生一个const引用。为了访问变量中的数据,将使用std::visit和规范的overload要获得const引用,只需为每种variantoverload(},},),storage非const引用的创建使用相同的技术,除了variant是之外,它不能产生非const引用。然而,当std::visitoverload(},},*/ }),storageoverload(},},),storage。原创 2024-04-16 13:40:11 · 1385 阅读 · 3 评论 -
C 语言中的多态性:结构体与函数指针的巧妙结合
多态性是面向对象编程中的一个重要概念,它允许不同类型的对象对相同的消息做出不同的响应。在 C 语言中,我们虽然没有类和对象的概念,但我们仍然可以通过结构体和函数指针来模拟多态性的行为。具体来说,我们可以通过定义一个包含函数指针的结构体,并在派生(子类)结构体中包含基类(父类)结构体的方式来实现多态性。 我们知道结构体的内存布局是连续的,结构体的成员按照定义的顺序依次存放在内存中。这为多态性的实现提供了基础,因为我们可以在派生类中包含基类的结构体,从而保证派生类对象的内存布局与基类对象兼容。原创 2024-04-15 23:36:10 · 1131 阅读 · 9 评论 -
C++可调用Callable类型的总结
可调用(Callable) 类型是可应用 INVOKE 操作(std::invoke 是在 C++17 里定义的类, 感觉意思就是执行函数操作的模板类.)原创 2024-04-12 21:29:09 · 1304 阅读 · 17 评论 -
C++继承多接口,调用虚函数跳转到错误接口的虚函数的奇怪问题
1) C++对象的内存模型(内存布局)2)数据转换static_cast的用法,延伸的还有const_cast、dynamic_cast、reinterpret_cast。原创 2024-04-11 23:05:15 · 1034 阅读 · 6 评论 -
C++17中的结构化绑定详解
总体而言,结构化绑定是C++17中一个非常有用的特性,它可以让我们的代码更简洁、易读,提高开发效率。通过结构化绑定,我们可以轻松地从数组、元组、结构体、类等多种数据结构中提取元素,并为它们分别赋予变量名。此外,结构化绑定还可以与范围for循环结合使用,简化对容器元素的处理。原创 2024-04-10 22:30:48 · 807 阅读 · 5 评论 -
C/C++中重载函数取地址的方法
上述代码[1],[2],[3]处都会出现编译错误,那是因为函数重载,多个函数名相同,找不到该用那个函数地址。这个时候解决办法就是人为指定用那个函数,那么人为指定用那个函数有哪些办法呢?原创 2024-03-29 15:06:41 · 991 阅读 · 18 评论 -
C++之std::mem_fn使用和实现原理(全)
允许我们将成员函数、数据成员或指向成员的指针转换为可调用的对象。这使得我们可以更加灵活地处理成员函数,特别是在需要将其作为回调或策略参数传递时。通过,我们可以避免直接使用函数指针或成员指针,从而简化代码并提高可读性。返回的对象可以存储并传递给其他函数或对象,以便稍后使用。需要注意的是,生成的函数对象需要接收一个对象实例作为第一个参数(对于非静态成员函数),然后才能调用该成员函数。对于数据成员,它返回的是对该成员的引用或指针,取决于数据成员的类型。原创 2024-03-27 14:51:39 · 1047 阅读 · 6 评论 -
C++手动实现定时器
skynet 是怎么样运转定时器的?skynet的 timer 线程会不断触发 expire_timer函数,在该函数中会不断执行timer_execute对 near 中的时器执行超时操作。执行完毕后,调用 timer_shift 从t[0] ~ t[3]中选择合适的定时器节点加入到 near 中,这过程就相当于提高了定时器节点的紧急程度(因为随着时间的流逝,定时器节点的紧急程度会越来越向near逼近)。转载 2024-03-24 13:45:04 · 1195 阅读 · 7 评论 -
std::thread使用及实现原理精讲(全)
线程创建和销毁是昂贵的操作,应尽量避免频繁创建和销毁线程。线程间共享数据时,要确保数据访问的线程安全性。尽量避免在多个线程中访问和修改全局变量或静态变量,除非这些变量是线程安全的。使用 std::thread 时,要确保在程序结束前对所有线程调用 join() 或 detach(),以避免资源泄漏。总之,std::thread 为 C++ 提供了强大而灵活的多线程支持,使得开发者能够更容易地编写并行程序。然而,多线程编程也带来了额外的复杂性和挑战,需要开发者仔细考虑线程间的数据共享和同步问题。原创 2024-03-22 22:44:41 · 2520 阅读 · 3 评论 -
有了std::thread,为什么还需要引入std::jthread?
C++11以来提供了C++原生的多线程std::thread,这极大的方便了多线程的书写。在此之前书写多线程时需要平台原生API,这对于跨平台尤其是跨多平台程序来讲,多线程部分代码书写及维护都是极大的工作量。}};运行如上代码时,会出现崩溃,堆栈信息如下:由如上堆栈信息可知,崩溃原因为std::thread在析构时,如果对象仍为joinable状态,则会触发中断,为避免崩溃需要在std::thread析构器前需要将其置于非joinable状态,即需要主动调用join或detach接口。原创 2024-03-20 15:58:04 · 1182 阅读 · 15 评论 -
C++14之std::index_sequence和std::make_index_sequence
std::index_sequence和std::make_index_sequence结合使用这种方法是模板元编程中的一个常见技巧,它允许我们在编译时生成和执行复杂的操作,而不需要在运行时付出任何额外的性能开销。原创 2024-03-12 08:17:18 · 1161 阅读 · 10 评论 -
C++无锁队列的原理与实现
生产环境中广泛使用生产者和消费者模型,要求生产者在生产的同时,消费者可以进行消费,通常使用互斥锁保证数据同步。但线程互斥锁的开销仍然比较大,因此在要求高性能、低延时场景中,推荐使用无锁队列。kfifo是Linux内核的一个FIFO数据结构,采用环形循环队列的数据结构来实现,提供一个无边界的字节流服务,并且使用并行无锁编程技术,即单生产者单消费者场景下两个线程可以并发操作,不需要任何加锁行为就可以保证kfifo线程安全。原创 2024-01-20 08:00:00 · 1664 阅读 · 5 评论 -
C++内存泄漏检测
使用宏定义,替换系统的内存分配接口。并利用__FILE__、__LINE__分别获取当前编译文件的文件名、行号,进行追踪位置信息。需要注意的是,宏定义一定要放在内存分配之前,这样预编译阶段才会替换为我们自己实现的_malloc和_free。原创 2024-03-07 07:42:39 · 1086 阅读 · 3 评论