自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(24)
  • 收藏
  • 关注

原创 C++类型转换

假设类A和B具有继承关系,基类A的指针可以接受子类B的地址,当运行func函数时,如果传入的是B的地址则没事,但如果传入的是A的地址,则程序会崩溃,因为A内没有_b,这是一个越界访问的问题。代码的运行结果如下。这是因为一开始ci是const类型变量,由于编译器认为ci的值不会被修改,因此会将ci的值预先加载到一块特定的寄存器中,后续每一访问ci时,不是通过访问虚拟地址空间,而是直接从cpu中的寄存器直接取值,后续ci的存储的值被修改为了20,但打印ci时,还是从寄存器中取因此会打印出10。

2024-05-13 18:59:15 391

原创 特殊类的设计与单例模式

(3)如果有多个单例类,假设它们之间有依赖关系,要求A单例先创建初始化,B单例再创建初始化,此处就不能使用饿汉,因为无法保证创建对象的顺序(静态对象是无法保证创建顺序的),这里要用懒汉模式,便于程序员手动控制。下面是一个简化的单例模式示例,为了支持单例的基本属性,要将构造函数设置为私有,将拷贝构造函数设置为delete,要通过静态成员函数GetInstance()来获取对象,如果不设置为静态成员函数,由于外部无法构造,而普通成员函数需要依靠对象来调用,因此无法获取对象。这里介绍一下单例模式。

2024-05-13 17:06:29 306

原创 c++智能指针

这样衍生出了一种新的写法,叫做智能指针,在动态开辟(new/malloc)空间时使用智能指针对象来接收指针,智能指针生命周期到了之后会自动调用析构函数,自动delete空间,一旦发生上面所说的异常问题,在跳出到外层栈空间时,new出的资源就会自动释放。auto_ptr采用的管理权转移,其实就是在发生拷贝构造或者赋值时,将原来的智能指针置空,这样对特定空间的管理权就由原对象转移到了新对象,这样原来的智能指针就不能进行解引用等指针操作了,这种管理权转移的做法是早期的设计缺陷,一般公司都明令禁止使用它。

2024-05-13 11:53:59 864

原创 C++11异常

如果不使用异常,而是选择返回一个值来标定错误的话,不好确定返回值,因为返回的值有可能就是除法的计算结果。异常的使用方法是,在可能发生错误的地方抛出异常,可能抛出异常的代码段要使用try与括号包裹运行,后续要使用catch以及{}捕捉异常,只要可能有异常抛出就一定要捕捉。发生异常时,调用的catch采取的是就近原则,如果是多层栈帧,会优先在内层的栈帧中寻找catch,如果没有就跳出到下一层寻找。(1)如果如果函数有返回值,在发生错误时,就有可能发生返回值与错误码相等的情况,很难处理。

2024-05-09 22:43:25 237

原创 Linux线程互斥与互斥量(锁)线程同步与条件变量

1、线程互斥的概念一个进程,在多个线程交叉执行的过程中,调度器会频繁地发生线程的调度和切换,可能会对全局变量的访问出现问题。线程一般在什么时候发生切换呢?有几种典型的情况,时间片到了,来了优先级更高的线程,线程等待的时候,在线程等待的时候,时间片没到,也没有优先级更高的线程,cpu也要进行线程的调度切换,因为当前进程在等待的时候不执行代码。从内核态返回用户态的时候,系统(内核态可以理解为操作系统的运行级别)会对线程的调度状态进行检测,如果可以,就直接发生线程切换。下面看一段代码感受一下会

2024-05-04 12:49:15 838 1

原创 c++11的一些特性和语法

就像下面这段代码,String有两个拷贝构造函数,如果传入的参数是左值(调用完还要接着用),就调用上面的构造函数,如果传入的是右值(调用完直接析构)就可以先将构造对象的地址赋值为空,在与将亡值交换,直接交换空间,效率更高。在PerfectForward()函数中,参数是一个右值,由于是函数模板,因此传参也可以传左值,在函数体内部调用Fun()函数,根据参数t的类型不同,调用重载的不同的Fun,可以看到不论传入的t是左值还是右值,调用的Fun都是左值的版本,这证明了t在下一层次的传参中发生了右值属性的丢失。

2024-04-25 00:17:49 792 1

原创 Linux线程(线程控制)

1、如何看待地址空间和页表? 在讲线程之前,先讲一下页表。在信号里有讲,页表分为用户级页表和内核级页表。页表其实还有很多其他的属性,如是否命中,RWX权限,,是用户的还是内核的,不论是用户级还是内核级页表,用的数据结构都是一样的,页表是需要被操作系统管理起来的,管理的方式就是先建立对应的数据结构再组织。基于之前所讲的知识,我们可以解释为什么运行如下代码会报错。对p解引用再赋值,本质上是找到p所指向的地址,即“hello world”所在的地址(常量区),需要做虚拟地址到物理地址的转化,再查

2024-04-22 21:39:31 862 1

原创 Linux进程信号(预备工作、信号产生、信号保存、信号处理)

完后在进程不会直接由内核态返回用户态,此时进程还处于一个拥有较高权限的状态,操作系统会通过CPU中的寄存器找到进程的PCB,进而对PCB中的三张表(pending位图,block位图,handle函数指针表)进行检测,先遍历block位图,如果为1,说明该信号被忽略,就不到pending中检测了,如果为0,就到pending中查看,pending中如果为1,说明收到该信号了,这时要去handler表中查看匹配的方法。所以处理信号时,如果是执行用户自定义的函数,进程需要先回到用户态,再处理信号,让信号递达。

2024-04-18 00:26:12 1003 1

原创 一个简易AVL树的实现

Insert分为两步,第一步,插入节点,具体操作和普通的搜索树类似,第二步,对插入之后的树进行调整。要知道,插入节点只会对由parent进行回溯的那一条链路产生影响,因此调整时,顺着parent的链路迭代调整。简易的代码实现,包括节点模板和树模板,节点多包含一个节点指针parent,所以树是一个三叉链。

2024-04-13 12:39:00 206

原创 c++,map与unordered_map的一些细节

可以看到operator[]的底层调用的是insert操作。而operator[]中就是获取insert的返回值再通过层层解引用,对iterator解引用得到一个pair,再返回pair中的second的引用,即返回value的引用,这就解释了为什么operator[]为什么既能够插入也能够修改value。我们知道,就算map中原来没有的key,在方括号操作后都会有。一段map类重载operator[]的代码。1、map中的[]方括号操作底层是怎么运作的。

2024-04-12 11:37:23 199

原创 一个简易的二叉搜索树实现

【代码】一个简易的二叉搜索树实现。

2024-04-11 11:27:54 166 1

原创 c++多态的一些细节

虚函数的重写(也称覆盖):在派生类中有一个跟父类完全相同的虚函数(即派生类虚函数与基类虚函数的函数名、返回值类型、参数列表完全相同),称子类的虚函数重写了基类的虚函数。注意,在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,派生类会从基类继承virtual关键字,也可以构成多态(但是不规范),如果基类的成员函数不加virtual,那么就算派生类的成员函数加了也不构成多态,这时候构成重定义(隐藏)。代码和执行结果如下。(2)被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

2024-04-09 18:54:24 399

原创 c++类在继承中的一些细节

在继承体系中父类和子类都有独立的作用域,子类和父类中有同名成员,子类将屏蔽对父类成员的直接访问,这一现象叫重定义,也称隐藏。当我们用一个子类对象对父类对象或者引用赋值时,只会赋值子类对象从父类对象中继承下来的成员变量的部分,同理,用一个子类对象的地址对一个父类对象指针进行赋值时,这个父类指针解引用获取的空间大小也是只有父类的大小。反过来,不能用父类的对象对子类对象赋值。继承是c++面向对象程序设计使代码可以复用的最重要的手段,它允许用户在保持原有类特性的基础上进行扩展,增加功能这样产生新的类,称为派生类。

2024-04-08 18:01:48 183

原创 更深层次谈c++模板(非类型模板参数,模板的特化,模板分离编译)

这是因为,编译器不会编译模板本身的,在预处理阶段,编译器检查我们主程序调用模板的地方进行特定实例化,如果模板的声明和定义分离,在定义的文件中如果没有调用模板,则不会实例化特定的函数或者类,在主程序中会链接不上。但是在进行字符串比较的时候,传入函数的参数时字符串的首地址,即两个地址进行比较,这显然是不正确的,我们希望的是堆字符串的内容进行比较。上面的为原始模板,第二个是一个偏特化(也称半特化)的模板,第三个是全特化的模板。以上的特化为全特化的情况,也可以有偏特化的情况,更加灵活。(2)增强了代码的灵活性。

2024-04-08 16:19:01 298

原创 优先级队列的简易实现

仿函数又叫函数对象,因为单看他的使用,你会以为他是一个函数,实际上是设计了一个类,这个类一般没有成员变量(所以类的大小为1字节),并且为这个类重载了括号运算符用于实现一些计算。以下是一个优先级队列的简易实现,包含一个堆的向下调整及向上调整函数,以及仿函数的利用。可以看到priority_queue有三个模板参数,第一个是元素类型T,第二个是容器类型container,第三个是用于进行比较的仿函数的类型,有大于和小于,通过控制仿函数的类型,我们可以控制vector是大堆还是小堆。

2024-04-08 11:49:12 186

原创 stack和queue的简易实现

可以看到stack和queue的模板有两个参数,第一个元素类型T,第二个容器类型,本质上stack和queue都是一个容器适配器,可以由多种容器支持。(2)对于queue类型,后面的容器传list可以,而传vector不行,queue的运行规则是先进先出,vector的头删操作太消耗时间,所以不被支持。deque是有一些局限性的,他的operator[]遍历效率很低。deque的迭代器包含四个变量,当前元素的指针,当前元素所在buffer的first和last指针,以及当前元素所在buffer的指针。

2024-04-07 20:37:06 203

原创 stl中list的模拟实现(简易实现),迭代器失效问题,list与vector的优劣。

(2)优势:1、可以根据下标随机访问,时间复杂度为O(1),极大提升了数据的访问效率,二分查找,基于堆的一些算法也是由这一特性支持的(如果在链表下效率极低)。对于链表这样的结构,元素之间由指针相连,在物理结构上不是线性的,所以不会发生vector中删除节点后,后面的节点的迭代器失效的情况,这也是链表在物理结构上的优势。(1)这样的数据结构的各元素之间有严格的顺序逻辑,在数据的删除操作时(erase时),后面的元素会往前移,会使被删除的元素的后面所有元素的迭代器失效,这时解引用程序会崩溃。

2024-04-07 14:27:28 200

原创 c++的一些细节(四)

二者的生命周期为整个源程序,不过全局变量的定义发生在main函数外部,静态变量的定义可以发生在任何地方(main函数内部,其他函数内部,类内部等)。静态变量的访问受其作用域的限制,函数体内定义的静态变量无法在函数外访问,类内定义的静态变量的访问需受到访问限定符的限制。c++中我们有用于开辟空间的新写法new,以及释放空间的写法delete,可以在开辟是进行内置类型的初始化,如果是自定义类型,则会调用构造函数,delete会调用析构函数。c++提供了以下写法。2、静态变量和全局变量的异同点。

2024-03-28 22:57:40 203

原创 c++类和对象中的一些细节(三)

这就造成我们的对象在调用operator时,不能通过传统的写法"cout<<对象"来进行调用,因为这个成员函数的第一个参数是this*指针,必须通过“对象<<cout”这种诡异的写法或者“对象.operator<<(cout)”来调用,二者都不具有较好的可读性。我们自己设计的Date类中包含了一个Time类,里面的时分秒变量是私有的,此时我们想写一个print函数将年月日时分秒都打印出来,就会受到访问限定符的限制,此时在Time类中用“friend class Date;4、内部类(一个较为冷门的概念)

2024-03-27 18:11:00 228

原创 求1+2+3+...+n

有这样一道编程题,要求不能使用乘除法,for,while,if,else,switch,case等关键字及条件判断语句(A?B:C),并且不能运用递归。在SumSolution里通过创建n个sum对象,调用n次sum的构造函数,在构造函数进行求值运算,充分利用了之前所学的static成员变量。

2024-03-27 16:34:33 84

原创 c++类和对象中的一些细节(二)

注意在func内对象obj调用print是会发生编译错误的,这是因为obj被const修饰,是一个只读的对象,obj.print()执行时,将obj的地址传给print,也就是print的第一个参数this,而这个this没有被const修饰,*this是可读可写的,发生了权限的扩大,这是不被编译通过的。我们的自定义类型的成员函数,如果不对自身成员变量进行写操作,都推荐在函数后边加上const关键字,这样外部的不论是带const还是不带const修饰的变量都可以调用这个成员函数。

2024-03-27 12:10:32 177

原创 c++类和对象中的一些细节。(一)

(2)其他成员变量要对齐到各自变量对齐数的整数倍的地址处,依次往下对齐。对于内置类型地成员变量没有做处理,对于自定义类型的成员变量,调用它的构造函数初始化,构造生成的对象的内置类型的成员变量还是随机值。在我们调用两个成员函数时,都将对象的地址传给了this指针,即NULL,执行print时,访问_a就是this->_a,对NULL解引用发生段错误,而执行show时,没有解引用操作所以正常运行。类的成员函数是保存在代码段的,一个类的不同对象在调用同一个方法是,访问的是同一块代码,减少资源的浪费。

2024-03-26 17:51:08 726

原创 c/c++基础知识(引用传参,引用返回值,内联函数,以及一些细节等等)

,这个tmp是一个临时变量,是会开辟空间以及发生拷贝的(只是在函数执行完毕,这块空间会自动释放,重置数值),并且这个临时变量具有常性,是不能用来初始化非常量引用的,所以后面的int& b = count1()会报错,如果我们在“int& b = count1()”前加上const则不会报错,常量初始化常量引用非常合理。总的来说传值和传引用的本质区别就是,传引用不开辟新的空间,不会发生数据的拷贝,从而减少时间和空间的浪费,不过要注意函数返回时传引用所用变量的声明周期,一不小心就会造成随机值。

2024-03-26 11:54:12 202

原创 c/c++基础知识杂谈(参数缺省,函数重载, 引用)

在c++代码中有时会看到在函数定义或者声明前面写extern "c" 的写法,其作用就是告诉编译器在编译过程中,不使用c++的函数修饰规则,而使用c的函数修饰规则,因此被写有extern "c"关键字的函数是无法与其他函数构成重载的。可以发现c++的命名是以前缀_Z 加函数名字符个数 加函数名 加每个参数类型的首字母来命名的,以此规则来命名避免函数的重复,而c的命名只有函数名,这解释了为什么c++能实现函数重载,而c却不行。在编码中,可能需要两个同名的函数,用来实现不同的功能,这便需要用到函数重载。

2024-03-24 23:42:09 250 1

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除