- 博客(30)
- 收藏
- 关注
原创 C++ 中面向对象编程如何实现动态绑定?
在 C++ 中,动态绑定(Dynamic Binding)是通过 虚函数(virtual function) 和 多态性(polymorphism) 来实现的。这是面向对象编程的重要特性之一,每个对象中都有一个指向虚函数表的指针(vptr)。当通过基类指针或引用调用虚函数时,程序会通过虚指针查找虚函数表中对应的函数地址,并调用实际的函数。编译器为每个含虚函数的类生成一个虚函数表(vtable),其中存储了该类中虚函数的地址。
2024-12-14 23:55:06
473
原创 力扣打卡14:多数元素
看了题解,有一种解法叫摩尔投票,可以解决这种问题,还是很巧妙的。它利用了不同数互相抵消,最后留下超过半数的数。这道题正常很简单,但是如果想要完成进阶做法有点困难。我使用了O(n/2)左右的空间,使用了哈希映映射。
2024-12-14 23:34:44
320
原创 C++ 中实现数据隐藏的方法
数据隐藏是面向对象编程的重要特性,它确保对象的内部状态只能通过受控的接口访问,从而提高代码的安全性和可维护性。通过这些方式,C++ 程序员可以有效地保护类的内部数据,同时提供清晰的访问接口以满足外部需求。
2024-12-12 22:31:16
406
原创 赋值运算符重载
作用:赋值运算符重载用于在一个对象已经被创建并赋值后,进行赋值操作。它允许我们自定义对象赋值的行为,特别是当对象涉及动态分配的资源时,默认的赋值行为可能会出现问题(例如浅拷贝),因此我们可以通过重载赋值运算符来实现深拷贝。
2024-12-11 21:27:47
556
原创 关于内联函数(实际使用的例子)
C++ 内联函数(inline function)是指在编译时将函数调用直接替换为函数体的代码,从而避免函数调用的开销。1.它通常用于短小的函数,以提高程序的执行效率。2.内联函数通过在函数声明前加上 inline 关键字来定义。3.使用内联函数可以减少函数调用的栈操作,但过度使用可能增加代码体积,导致性能下降。
2024-12-09 21:44:09
2902
原创 力扣打卡11:合并区间(比较器内联,引用传参的优化)
我首先用了自己写的静态比较器(因为sort不是类内函数,cmp如果不是静态,就会报错)(将cmp写在类外也行),但是这样的话,排序的每次比较,都会调用函数,造成开销,同时是值传递,会复制值,造成开销。当前right
2024-12-09 21:30:09
683
原创 纯虚函数和抽象类
定义:抽象类是包含至少一个纯虚函数的类。一个包含纯虚函数的类不能被实例化,因此,它是一个不完整的类。如果一个类包含至少一个纯虚函数,那么这个类就是抽象类,不能直接实例化。抽象类可以包含其他普通的成员函数和成员变量,也可以包含有实现的虚函数。抽象类可以作为其他类的基类,提供接口或共享实现。public:// 纯虚函数void commonMethod() { // 普通成员函数// 一些实现。
2024-12-08 22:16:00
610
原创 力扣打卡10:K个一组翻转链表
乍一看好像比较容易,其实有很多细节。比如每一组反转后怎么找到上一组的新尾,怎么找到下一组的新头。并且进阶做法需要空间O(1)。1.用l,r指针作移动窗口,划定当前组(通过固定l,移动r)这道题需要在链表上,每k个为一组,翻转,链接。再注意一些长度不足,第一组等细节,就可以解题。4.将上一个组的尾连接当前组的新头。2.将本组的头,尾结点记录。
2024-12-08 22:03:59
705
原创 关于多态(静态,动态)
多态性(Polymorphism)是面向对象编程中的一个基本概念,指的是同一操作或方法可以作用于不同类型的对象,并产生不同的行为。在不同的上下文中,"多态"意味着“多种形态”。
2024-12-07 21:27:59
683
原创 力扣打卡9:重排链表
3.从两个链表的头开始遍历链表,将后段的链表一个结点一个结点插入前段的链表。这是一道操作链表的题。按照要求,我们可以将解题的步骤分成三步。1.找链表中间结点(我使用了快慢指针寻找),并断开。2.现在有2链表,将后段链表进行翻转操作。
2024-12-07 21:17:26
375
原创 关于继承以及父类指针调用方法
继承性是面向对象编程(OOP)的三大基本特性之一(另外两个是封装和多态)。继承允许一个类(子类)从另一个类(父类或基类)中获取属性和方法,从而实现代码重用和扩展。
2024-12-06 23:49:45
608
原创 力扣打卡8:最长上升子序列
看到进阶解法可以O(nlogn),想到可能是要用到二分,但是,我想到的是和map排序,再二分查找第一个比当前值小的数,再找比它小的所有数,中维护max序列,再塞到map中,可惜严格来讲还是O(n^2)。看完官方题解,才明白要贪心来贪最小的位置,再结合二分查找,维护一条最长最优的序列。本题我开始想到的是dp,复杂度为O(n^2),这也是很经典的解法。
2024-12-06 17:17:07
443
原创 力扣打卡7:螺旋矩阵
正常可能需要4个for循环,我只使用了2个for,通过r1,r2反复乘-1,可以控制4个方向。其实本质与4个for的原理类似,但这样会使我的代码更简短。这道题,盯住遍历的目前的位置,控制四个方向的边界,注意细节就可以解。
2024-12-05 23:31:28
235
原创 封装以及getter和setter怎么提高代码的灵活性和安全性
封装性是面向对象编程的一个重要特性,它指的是将数据和操作(方法)封装到对象中,并通过限定访问权限来保护数据的完整性和安全性。这样可以隐藏对象的内部实现细节,仅暴露必要的接口,从而提高代码的可维护性和复用性。
2024-12-05 23:10:30
687
原创 力扣打卡6:反转链表II
其实就是一直向前移动指针,走到left停下,将后面的链表实时翻转,走到right就停止,并将这段反转的部分的头和尾分别连到left前一个结点和right后一个结点之前的位置。链表的翻转是很常见的链表题型,但题目要求将指定位置翻转,并最好一趟做完。你可以使用一趟扫描完成反转吗?
2024-12-04 21:09:07
364
原创 力扣打卡5:LRU缓存
但是put不太好优化成O(1),我使用了一个队列和一个哈希表来实现它。通过记录一个数据的使用次数存在哈希表,数据存储顺序放在queue,来实现这个put。我很喜欢官方题解的方式,他使用了一个哈希表和一个双向链表,并将它们组合成一个新的数据结构,哈希表key为数据的索引,value是链表结点的指针,链表中存放数据。get可以利用哈希表的性质实现快速查询,put可以利用双端链表的特性,可以灵活的将对应结点移到队首,将队尾的删除。这道题我解法的put函数严格意义上讲不是O(1),不如力扣官方的题解。
2024-12-03 22:31:51
377
原创 new delete & malloc free
总结来说,malloc 和 new 分配内存时的最大限制受操作系统、硬件和进程的虚拟内存限制等多方面因素的影响,超出限制时它们会返回 NULL 或抛出异常。new 和 delete 底层代码调用了malloc 和 free。泛用性不如malloc和free。1.new返回值不需强转,而malloc返回值需要(malloc返回值是void*)4.new先调用malloc再调用构造函数(delete先析构,再free)3.new不需要传入具体字节,malloc需要。5.new会抛出异常,malloc返回空。
2024-12-03 19:20:19
918
原创 力扣打卡4(二叉树的最近公共祖先)
还有一些进阶的方法来解决这个问题,比如使用ST表,但是那些是针对于多次查询静态树的方法,在这里使用不太合适,而且ST表建表是O(nlogn),比线性的复杂度高,因此这里不太适合ST表。通过BFS遍历树,将每个节点的父节点,存入哈希映射中(key为节点,value为其父节点),再以两个给的节点作为起点,回溯找其公共最近的父节点输出。2.在回溯找祖先时,我根据二者层的深度,优先回溯深层的,因为这可以更快的找到其最近的祖先,相比于使用hashset。这是一道LCA问题,同时,是一道只查询一次的最近公共祖先。
2024-12-02 23:44:47
242
原创 关于类型转换以及其可能遇到的问题
如果进行类型转换时不满足某些规则,或者转换是非法的,可能会导致未定义行为。C++ 对这种情况没有提供明确的错误或警告,可能导致程序崩溃、数据破坏等问题。// 未定义行为,转换不合法非法指针转换:使用 reinterpret_cast 进行不正确的指针转换时,可能会产生不可预料的结果,甚至可能破坏程序的内存结构。对象转换不合法:在继承关系中使用不正确的转换(例如,基类指针强制转换为与之不相关的派生类指针),会导致程序崩溃或访问非法内存。
2024-12-02 23:17:29
2298
原创 力扣打卡3
对于这道题,是在层次遍历的基础上做了简单的变形。1.可以在每次遍历一层的时候,将其存入vector中,根据奇数还是偶数对其进行翻转,再存入答案的二维vector中。2.可以用双端队列,如果奇数层,存队尾,取队首。反之存队首,取队尾。这是我第一次想到以及使用双端队列。有所收获。
2024-12-01 23:57:19
217
原创 关于函数重载
编译器在编译期间,根据调用的函数名和参数列表,选择合适的函数进行调用。函数重载(Function Overloading)是 C++ 中的一种特性,允许在同一个作用域内定义多个函数,这些函数具有相同的名字但参数列表不同。编译器通过函数的参数个数、类型、顺序来区分它们,并调用对应的函数。(重载只和名字有关,与函数的返回值,函数类型无关)C++ 编译器在实现函数重载时,通过 名字修饰(Name Mangling)机制为每个函数生成唯一的标识符。在链接过程中,编译器利用这些修饰后的名称区分不同的函数。
2024-12-01 23:47:21
834
原创 关于内存对齐
内存对齐是计算机系统中为优化性能和确保数据正确访问而对变量的存储地址进行的一种约束。内存对齐的目的是使数据按照特定的规则存储在内存中,从而提高访问速度并减少潜在的硬件问题。
2024-12-01 00:16:12
833
原创 力扣打卡2
这道题完美解法应该是,二分法。二分法的原理很简单,但是实际编写的时候,其边界判断以及寻找有时会非常麻烦,这道题我加很多特判,判着判着就蒙了,乱了。后来可以说好不容易才对。对于二分还是需要加强练习的。
2024-11-30 22:56:40
246
原创 volatile关键字
C 和 C++ 中,volatile 关键字是一种类型修饰符,主要用于修饰变量,告诉编译器该变量的值可能随时被外部环境(如硬件或其他线程)修改。因此编译器在优化代码时需要特别注意以下几点:作用和用途编译器通常会对代码进行优化,如果某个变量看起来没有被显式修改,编译器可能会优化掉对该变量的多次读取。如果没有 volatile 修饰,编译器可能会认为 flag 在循环中没有改变,于是将其值缓存在寄存器中,导致程序进入死循环。编译器会保证每次读取 flag 都是从内存中读取,而不是从寄存器中读取。
2024-11-30 22:50:39
413
原创 右值引用和移动语义
它为实现移动语义提供了基础,允许资源的所有权从一个对象“移动”到另一个对象,而不是进行昂贵的复制。C++中的移动语义(Move Semantics)是C++11引入的一种优化技术,旨在提高程序的性能,特别是在涉及大数据量对象时。它通过允许资源(如内存或文件句柄等)从一个对象转移到另一个对象,避免了不必要的深拷贝,从而减少了资源的复制和释放开销。在传统的拷贝语义中,当对象被赋值或传递时,通常会执行对象的深拷贝。而移动语义允许将资源的所有权“移动”到新对象,而不是复制内容,避免了不必要的开销。
2024-11-29 20:39:12
2015
原创 关于C++的自动类型推导auto 和 decltype
使用 尾置返回类型(trailing return type)的语法,指定返回值的类型为 decltype(a + b)。decltype 是一种编译时的类型推导工具,它用于获取某个表达式的精确类型,而不需要创建或初始化变量。在这种写法下,尾置返回类型不再是必需的,但 C++11 中仍然需要它来指定复杂返回类型。返回类型可能很复杂,且依赖于模板参数,因此需要一种机制基于表达式结果推导返回类型。用于声明函数的返回值类型,表示类型由编译器自动推导。类型获取、模板返回类型、复杂类型声明等。不保留引用(值传递)
2024-11-29 20:26:07
492
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅
1