STL 源码剖析:sort
我大抵是太闲了。
更好的源码讨论阅读体验。
sort 作为最常用的源码讨论 STL 之一,大多数人对于其了解仅限于快速排序。源码讨论
听说其内部实现还包括插入排序和堆排序,源码讨论java源码 类图于是源码讨论很好奇,决定通过源代码一探究竟。源码讨论
个人习惯使用 DEV-C++,源码讨论不知道其他的源码讨论编译器会不会有所不同,现阶段也不是源码讨论很关心。
这个文章并不是源码讨论析完之后的总结,而是源码讨论边剖边写。不免有个人的源码讨论猜测。而且由于本人英语极其差劲,源码讨论大抵会犯一些憨憨错误。
源码部分sort
首先,在 Dev 中输入以下代码:
然后按住 ctrl,鼠标左键sort,就可以跳转到头文件 stl_algo.h,并可以看到这个:
注释、模板和函数参数不再解释,我们需要关注的是函数体。
但是,中间那一段没看懂……
点进去,是一堆看不懂的#define。
查了一下,感觉这东西不是我这个菜鸡能掌握的。
有兴趣的 戳这里。
那么接下来,就应该去到函数__sort 来一探究竟了。
__sort
通过同样的方法,继续在stl_algo.h 里找到 __sort 的源代码。
同样,只看函数体部分。
一般来说,sort(a,a+n) 是对于区间 [公式] 进行排序,所以排序的前提是 __first != __last。
如果能排序,那么通过两种方式:
一部分一部分的看。
__introsort_loop
最上边注释的翻译:这是排序例程的帮助程序函数。
在传参时,除了首尾迭代器和排序方式,还传了一个std::__lg(__last - __first) * 2,对应 __depth_limit。
while 表示,当区间长度太小时,不进行排序。java源码+blob
_S_threshold 是一个由 enum 定义的数,好像是叫枚举类型。
当__depth_limit 为 [公式] 时,也就是迭代次数较多时,不使用 __introsort_loop,而是使用 __partial_sort(部分排序)。
然后通过__unguarded_partition_pivot,得到一个奇怪的位置(这个函数的翻译是无防护分区枢轴)。
然后递归处理这个奇怪的位置到末位置,再更新末位置,继续循环。
鉴于本人比较好奇无防护分区枢轴是什么,于是先看的__unguarded_partition_pivot。
__unguarded_partition_pivot
首先,找到了中间点。
然后__move_median_to_first(把中间的数移到第一位)。
最后返回__unguarded_partition。
__move_median_to_first
这里的中间数,并不是数列的中间数,而是三个迭代器的中间值。
这三个迭代器分别指向:第二个数,中间的数,最后一个数。
至于为什么取中间的数,暂时还不是很清楚。
`__unguarded_partition`
传参传来的序列第二位到最后。
看着看着,我好像悟了。
这里应该就是实现快速排序的部分。
上边的__move_median_to_first 是为了防止特殊数据卡 [公式] 。经过移动的话,第一个位置就不会是最小值,放在左半序列的数也就不会为 [公式] 。
这样的话,__unguarded_partition 就是快排的主体。
那么,接下来该去看部分排序了。
__partial_sort
这里浅显的理解为堆排序,至于具体实现,在stl_heap.h 里,不属于我们的讨论范围。
(绝对不是因为我懒。)
这样的话,__introsort_loop 就结束了。下一步就要回到 __sort。
__final_insertion_sort
其中某常量为enum { _S_threshold = };。
其中实现的java源码版权函数有两个:
__insertion_sort
其中的__comp 依然按照默认排序方式 < 来理解。
_GLIBCXX_MOVE_BACKWARD3
进入到_GLIBCXX_MOVE_BACKWARD3,是一个神奇的 #define:
其上就是move_backward:
上边的注释翻译为:
__unguarded_linear_insert
翻译为“无防护线性插入”,应该是指直接插入吧。
当__last 的值比前边元素的值小的时候,就一直进行交换,最后把 __last 放到对应的位置。
__unguarded_insertion_sort
就是直接对区间的每个元素进行插入。
总结
到这里,sort 的源代码就剖完了(除了堆的那部分)。
虽然没怎么看懂,但也理解了,sort 的源码是在快排的基础上,通过堆排序和插入排序来维护时间复杂度的稳定,不至于退化为 [公式] 。
鬼知道我写这么多是为了干嘛……
源码视角,Vue3为什么推荐使用ref而不是reactive
ref和reactive是Vue3中实现响应式数据的核心API。ref用于封装基本数据类型,而reactive用于处理对象和数组。尽管reactive似乎更适合处理对象,但Vue3官方文档更推荐使用ref。
官方文档指出,ref比reactive更适用。下面我们从源码的角度详细讨论这两个API,以及Vue3为什么推荐使用ref而不是reactive。
ref的内部工作原理是,它是一个函数,接受一个内部值并返回一个响应式且可变的引用对象。这个引用对象有一个.value属性,指向内部值。
在上述代码中,ref函数通过new RefImpl(value)创建了一个新的RefImpl实例。这个实例包含getter和setter,分别用于追踪依赖和触发更新。使用ref可以声明任何数据类型的响应式状态,包括对象和数组。
ref的核心是返回响应式且可变的引用对象,而reactive的核心是返回响应式代理,这是两者本质上的核心区别,也就导致了ref优于reactive。
reactive的内部工作原理是,它是一个函数,接受一个对象并返回该对象的响应式代理,也就是Proxy。
reactive的源码相对简单,通过new Proxy(target, baseHandlers)创建了一个代理。这个代理会拦截对目标对象的操作,从而实现响应式。闲聊牛牛源码
ref和reactive在声明数据的响应式状态上,底层原理不同。ref采用RefImpl对象实例,reactive采用Proxy代理对象。
当你使用new RefImpl(value)创建一个RefImpl实例时,这个实例大致上会包含以下几部分:Dep类负责管理一个依赖列表,并提供依赖收集和通知更新的功能。RefImpl类包含一个内部值_value和一个Dep实例。当value被访问时,通过get方法进行依赖收集;当value被赋予新值时,通过set方法触发更新。
尽管两者在内部实现上有所不同,但它们都能满足我们对于声明响应式变量的要求,但是reactive存在一定的局限性。
reactive的局限性包括仅对引用数据类型有效,使用不当会失去响应。reactive主要适用于对象,包括数组和一些集合类型(如Map和Set)。对于基础数据类型(如string、number和boolean),reactive是无效的。这意味着如果你尝试使用reactive来处理这些基础数据类型,将会得到一个非响应式的对象。
ref()为响应式编程提供了一种统一的解决方案,适用于所有类型的数据,包括基本数据类型和复杂对象。以下是推荐使用ref的几个关键原因:统一性、深层响应性和灵活性。
ref的核心优势之一是它的统一性。它提供了一种简单、一致的方式来处理所有类型的数据,无论是数字、字符串、对象还是数组。这种统一性极大地简化了开发者的代码,减少了在不同数据类型之间切换时的复杂性。
ref支持深层响应性,这意味着它可以追踪和更新嵌套对象和数组中的变化。这种特性使得ref非常适合处理复杂的数据结构,如对象和数组。
ref提供了高度的灵活性,尤其在处理普通赋值和解构赋值方面。这种灵活性使得ref在开发中的使用更加方便,特别是在进行复杂的数据操作时。
ref在Vue3中提供了一种更统一、灵活的响应式解决方案,还能避免了reactive的网站源码+app某些局限性。希望这篇文章对你有所帮助,有所借鉴。
国外有哪些网站源码分享论坛博客?
国外有许多网站源码分享的论坛和博客,搜索这些资源可以帮助你找到合适的平台。在这些平台中,你可以找到大量的开源代码、教程、讨论和项目分享。以下是一些知名的国外网站源码分享论坛博客:
1. CSDN博客: blog.csdn.net
2. 源码之家: ymzhao.com
3. 博客园: cnblogs.com
4. CTO博客: blog.cto.com
在寻找合适的博客站点时,可以浏览这些平台,查看它们提供的内容和社区氛围。中国的博客站点如新浪博客、网易博客、搜狐博客、百度空间和人民网博客,也提供免费的个人博客服务,并且各有特色。
此外,还有多种免费或付费的在线论坛专注于网站源码分享,包括:
1. sitepoint.com/
2. quora.com/
3. webmasterworld.com/
4. reddit.com/r/webdev/
对于开源数据库及CMS系统,以下网站是值得参考的资源:
1. MySQL: mysql.com/
2. PostgreSQL: postgresql.org/
3. SQLite: sqlite.org/
4. MongoDB: mongodb.com/
5. Redis: redis.io/
6. CouchDB: couchdb.apache.org/
通过搜索这些资源和平台,你可以找到适合自己需求的网站源码分享论坛博客。
ReentrantLock源码详细解析
在深入解析ReentrantLock源码之前,我们先了解ReentrantLock与同步机制的关系。ReentrantLock作为Java中引入的并发工具类,由Doug Lea编写,相较于synchronized关键字,它提供了更为灵活的锁管理策略,支持公平与非公平锁两种模式。AQS(AbstractQueuedSynchronizer)作为实现锁和同步器的核心框架,由AQS类的独占线程、同步状态state、FIFO等待队列和UnSafe对象组成。AQS类的内部结构图显示了其组件的构成。在AQS框架下,等待队列采用双向链表实现,头结点存在但无线程,T1和T2节点中的线程可能在自旋获取锁后进入阻塞状态。
Node节点作为等待队列的基本单元,分为共享模式和独占模式,值得关注的是waitStatus成员变量,它包含五种状态:-3、-2、-1、0、1。本文重点讨论-1、0、1状态,-3状态将不涉及。非公平锁与公平锁的差异在于,非公平锁模式下新线程可直接尝试获取锁,而公平锁模式下新线程需排队等待。
ReentrantLock内部采用非公平同步器作为其同步器实现,构造函数中根据需要选择非公平同步器或公平同步器。ReentrantLock默认采用非公平锁策略。非公平锁与公平锁的区别在于获取锁的顺序,非公平锁允许新线程跳过等待队列,而公平锁严格遵循队列顺序。
在非公平同步器的实例中,我们以T1线程首次获取锁为例。T1成功获取锁后,将exclusiveOwnerThread设置为自身,state设置为1。紧接着,T2线程尝试获取锁,但由于state为1,获取失败。调用acquire方法尝试获得锁,尝试通过tryAcquire方法实现,非公平同步器的实现调用具体逻辑。
在非公平锁获取逻辑中,通过CAS操作尝试交换状态。交换成功后,设置独占线程。当当前线程为自身时,执行重入操作,叠加state状态。若获取锁失败,则T2和T3线程进入等待队列,调用addWaiter方法。队列初始化通过enq方法实现,enq方法中的循环逻辑确保线程被正确加入队尾。新线程T3调用addWaiter方法入队,队列初始化完成。
在此过程中,T2和T3线程开始自旋尝试获取锁。若失败,则调用parkAndCheckInterrupt()方法进入阻塞状态。在shouldParkAfterFailedAcquire方法中,当前驱节点等待状态为CANCELLED时,方法会找到第一个非取消状态的节点,并断开取消状态的前驱节点与该节点的连接。若T5线程加入等待队列,T3和T4线程因为自旋获取锁失败进入finally块调用取消方法,找到等待状态不为1的节点(即T2),断开连接。
理解了shouldParkAfterFailedAcquire方法后,我们关注acquireQueued方法的实现。该方法确保线程在队列中正确释放,如果队列的节点前驱为head节点,成功获取锁后,调用setHead方法释放线程。setHead方法通过CAS操作更新head节点,释放线程。acquire方法中的阻塞是为防止线程在唤醒后重新尝试获取锁而进行的额外阻断。
锁的释放过程相对简单,将state减至0,将exclusiveOwnerThread设置为null,完成锁的释放。通过上述解析,我们深入理解了ReentrantLock的锁获取、等待、释放等核心机制,为并发编程提供了强大的工具支持。
Vue3源码细读——ref
深入解析Vue3中ref的实现细节
在Vue3源码中,ref相关功能主要集中在'packages/reactivity/src/ref.ts'文件里。
在该文件中,ref的使用与处理主要依赖于最后一个函数的调用:`createRef(value, false)`。通过此函数,可以创建或更新ref实例。
接下来,让我们深入探讨`createRef`函数。它首先判断传入的参数是否已经是一个ref实例,如果是,则直接返回;否则,将返回一个`RefImpl`实例。进一步了解`RefImpl`构造函数,我们发现它包含了`isShallow`和`isReadonly`两个关键属性,它们负责判断ref实例的浅度和是否为只读。
通过阅读源码,我们了解到在控制台log中出现的`_value`和`_rawValue`函数。这些函数用于方便进行判断和对比,尤其是`_rawValue`记录了ref的原始值,以避免不必要的更新,比如在值未发生变化时,节省了性能损耗。实践一下,例如页面上的button点击修改值,然后使用watch监控ref,你会发现watch并未执行。
在源码中,还隐含了`trackRefValue`和`triggerRefValue`两个函数。它们分别在`ref.ts`文件内声明,分别负责跟踪和触发ref值的变化。
让我们继续深入到`trackRefValue`函数,它主要负责跟踪ref值的变化。`activeEffect`的概念在这里出现,它在ref的读取操作中扮演关键角色。当值发生变化时,Vue通过关联`activeEffect`实现响应式更新。具体来说,当我们进行第一次读取时,会将这种关联关系存储起来(通常使用Set数据结构)。改变值时,通过这些关联进行更新(响应式),达到响应式效果。
至此,关于Vue3中ref源码的解读暂时告一段落。如果有任何错误或需要进一步讨论的地方,欢迎大神们指出,同时,我也期待着自己的进步。
React事件机制的源码分析和思考
本文探讨了React事件机制的实现原理及其与浏览器原生事件机制的异同。基于React版本.0.1,本文对比了与.8.6版本的不同之处,深入分析了React事件池、事件代理机制和事件触发过程。
在原生Web应用中,事件机制分为事件捕获和事件冒泡两种方式,以解决不同浏览器之间的兼容性问题。事件代理机制允许事件在根节点捕获,然后逐层冒泡,从而减少事件监听器的绑定,提升性能。
React引入事件池概念,以减少事件对象的创建和销毁,提高性能。然而,在React 中,这一概念被移除,事件对象不再复用。React内部维护了一个全局事件代理,通过在根节点上绑定所有浏览器原生事件的代理,实现了事件的捕获和冒泡过程。事件回调的执行顺序遵循捕获-冒泡的路径,而事件传播过程中,React合成事件对象与原生事件对象共用。
React合成事件对象支持阻止事件传播、阻止默认行为等功能。在React事件内调用`stopPropagation`方法可以阻止事件的传播,同时`preventDefault`方法可以阻止浏览器的默认行为。在实际应用中,需注意事件执行的顺序和阻止行为的传递。
文章最后讨论了React事件机制的优化和调整,强调了React对事件调度的优化,并提供了对不同事件优先级处理的指导。通过对比不同版本的React,本文为理解React事件机制提供了深入的见解。
有哪些开源的源码网站?
开源源码网站为开发者提供了丰富的资源和交流平台。下面列举了一些知名的开源源码网站:
一、CSDN - 专业开发者社区,提供原创博客、问答、培训、论坛和资源下载服务。
二、OSCHINA - 中文开源技术交流社区,传播开源理念,推广开源项目,提供开源技术交流平台。
三、SegmentFault - 中国领先的新一代开发者社区和技术媒体,提供问答、专栏、课程和资讯。
四、v2ex - 创意工作者社区,讨论编程、设计、硬件、游戏等话题。
五、有穹 - 专注开源软件源码分享与交流的平台,发布、收藏和下载源码作品。
六、LearnKu - 终身编程者知识社区,定制编程知识。
七、掘金 - 技术文章社区,提供技术筛选和干货分享。
八、博客园 - 开发者知识分享社区,帮助开发者分享和学习。
九、Gitee - Git代码托管和协作平台,提供代码托管服务。
十、GitHub - 全球最大的开源社区,聚集众多开源项目。
这些网站为开发者提供了丰富的资源和交流机会,是学习和分享编程知识的重要平台。
源码论坛哪个好?
源码论坛中,GitHub和CSDN是两个值得推荐的论坛。
GitHub不仅是一个代码托管平台,更是一个开源社区。它拥有庞大的用户群体和丰富的项目资源,涵盖了各种编程语言和技术领域。在GitHub上,你可以找到大量开源项目的源码,同时也可以参与开源项目的开发和讨论。GitHub的社区氛围活跃,有很多技术大神在这里分享他们的经验和见解,对于学习源码和提升技术能力非常有帮助。
CSDN是中国最大的IT社区之一,拥有庞大的用户群体和丰富的技术资源。在CSDN上,你可以找到各种编程语言的源码和学习资料,同时也可以参与技术讨论和交流。CSDN的论坛区域非常活跃,有很多技术高手在这里分享他们的经验和心得,对于解决技术问题和提升技术水平非常有帮助。
此外,这两个论坛都提供了丰富的功能,如源码下载、在线编辑、版本控制等,方便用户进行源码的学习和分享。同时,它们的搜索功能也非常强大,可以帮助用户快速找到需要的技术资料和源码。
总的来说,GitHub和CSDN都是源码论坛中不错的选择。它们都有丰富的技术资源和活跃的社区氛围,可以帮助你提升技术能力和解决技术问题。当然,除了这两个论坛外,还有很多其他的源码论坛也值得一试,你可以根据自己的需求和兴趣进行选择。
2024-11-23 08:14
2024-11-23 08:02
2024-11-23 07:35
2024-11-23 06:57
2024-11-23 06:39