QINPENG

虚函数与多态

多态有编译时的多态性:通过函数的重载和运算符的重载实现,也就是函数地址在编译时确定,
叫做静态绑定。
运行时的多态性:在程序执行前,无法根据函数名和参数来确定该调用哪个函数,
必须在程序执行过程中,根据执行的具体情况来动态的确定。
也就是函数地址在运行时才确定的,叫做动态绑定。
它是通过类继承关系和虚函数来实现的,比如基类指针指向子类对象,调用相应的虚函数,
指向不同的子类实现不同的方法。

关键字virtual指明该函数是虚函数,virtual只能用于在类的内部声明,
若函数在类外部实现则不需要再加virtual关键字。
一个基类的虚函数允许子类去重写他,该函数始终保持虚函数的特征。
每个对象有一个虚指针指向一个虚表,每个类有一个虚函数表,存放这个类的虚函数地址,派生类会继承基类的虚函数
派生类和基类指向的虚函数地址是相同的,但是如果派生类重写了基类的虚函数,
那么就会覆盖掉派生类原来的对应的虚函数地址。
使用虚函数要注意一些问题:
1.只有类的成员函数才能声明为虚函数;
2.静态成员函数不可以为虚函数,因为他是所有同一类对象共有,不受限于某个对象。
静态成员函数的多态可以通过重载来实现。
3.内联函数不能为虚函数,内联函数每个对象一个拷贝,无映射关系。
4.析构函数可定义为虚函数,构造函数不可以定义为虚函数,
因为在调用构造函数时对象还没有完成实例化。在基类中及其派生类中都动态分配内存空间时,
必须把析构函数定义为虚函数,实现撤销对象时的多态性。
5.一个类成员函数可以同时声明为模板函数和虚函数。
纯虚函数是在虚函数后面加”=0“这样就声明了纯虚函数,一般不允许有函数实体。
表明了该类是抽象类,只能被继承,不能被实例化,纯虚函数必须在派生类中实现,
但是纯虚析构函数需要提供函数实现,因为析构一个类的过程中会把所有的父类全析构了。

static

修饰全局变量时,用于限制该全局变量的使用范围。仅能在本文件内使用该变量。
修饰局部变量时,普通局部变量存储在栈区,当函数执行结束后,就会被清空。
静态局部变量存储在静态区,当函数执行结束后,不会被清空。下次再次执行函数时,能保持上一回的值。
修饰成员变量或者函数时,静态成员变量和静态成员函数不答属于某一个对象,而是属于同一类的所有对象。可以使用类名::成员/方法 的方式进行使用。
static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化时分配,
所以初始化必须在类的外部初始化,初始化时不用加static修饰。
static成员函数,没有this形参,因为它不属于某一个对象,static成员函数里面只能访问static成员变量。
static成员函数不能声明为虚函数,不能实现动态绑定
static成员函数既可以在类的内部定义,也可以在类的外部定义,在外部定义时,不能重复指定static保留字。
static 成员函数不能声明为const,因为const是针对this是不是const而言的
构造函数、析构函数不能为静态函数。

const

1)欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了;

2)对指针而言,const可以修饰指针所指向的变量,比如const int *p,表明不可以改变p所指向的变量的值,
但是可以改变指针的值,const 可以修饰指针,比如int *const p;表明不可以改变p的值,可以改变p所指向的变量的值。

3)在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值;

4)对于类的成员函数,有时候必须指定其为const类型,表明其是一个常函数,不能修改类的成员变量;
如果实在时要修改某一个成员变量的值,可以在声明这个变量的时候加mutable关键字。表示可变的。

5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。

内联函数

为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,
表示为内联函数。
它可以像普通函数一样被调用,但是在调用时并不通过函数调用的机制而是通过将函数体直接插入调用处来实现的,这样可以大大减少由函数调用带来的开销,从而提高程序的运行效率。一般来说inline用于定义类的成员函数。

堆和栈区别

1)申请方式:
栈由系统自动分配和管理,堆由程序员手动分配和管理。
2)效率:
栈由系统分配,速度快,不会有内存碎片。
堆由程序员分配,速度较慢,可能由于操作不当产生内存碎片。
3)扩展方向
栈从高地址向低地址进行扩展,堆由低地址向高地址进行扩展。
4)程序局部变量是使用的栈空间,new/malloc动态申请的内存是堆空间,
函数调用时会进行形参和返回值的压栈出栈,也是用的栈空间。

new/malloc delete/free

1、new/delete是C++的操作符,而malloc/free是C中的函数。
new/delete是保留字,不需要头文件支持;malloc/free需要头文件库函数支持。
2、new做两件事,一是分配内存,二是调用类的构造函数;同样,
delete会调用类的析构函数和释放内存。而malloc和free只是分配和释放内存。
3、new建立的是一个对象,而malloc分配的是一块内存;new建立的对象可以用成员函数访问,不要直接访问它的地址空间;
malloc分配的是一块内存区域,用指针访问,可以在里面移动指针;new出来的指针是带有类型信息的,而malloc返回的是void指针。

指针和引用

图片说明

强制转换

图片说明

智能指针

图片说明
图片说明
图片说明
图片说明
图片说明
图片说明
图片说明

内存基本构造

图片说明
图片说明

为什么子类对象可以赋值给父类对象而反过来却不行

子类继承于父类,它含有父类的部分(除了父类私有属性和私有方法),又做了扩充。
如果子类对象赋值给父类变量,则使用该变量只能访问子类的父类部分(因为子类含有父类的部分,所以不会有问题)
但是,如果反过来,这个子类变量如果去访问它的扩充成员变量,就会访问不到,因为原变量不包含该部分,会内存越界。
指针也遵循这个原则,子类对象的指针可以用父类对象指针来接收赋值,但反过来不行
(常用于多态,父类对象指针(或引用)来接收子类对象指针(或引用),进而实现多态性。

文件编译过程

图片说明

malloc原理

图片说明
图片说明

死锁

所谓死锁,是指两个或两个以上并发进程彼此互相等待对方所拥有的资源,且这些并发进程在得到对方的资源之前不会释放自己所拥有的资源。从而造成大家都想得到资源而又都得不到资源,各并发进程不能继续向前推进的状态。

产生死锁原因:
(1)并发进程的资源竞争。产生死锁的根本原因在于系统提供的资源个数少于并发进程所要求的该类资源数。
(2)进程推进顺序不当引起死锁
产生死锁必要条件:
(1)互斥:竞争的资源一次只能被一个进程使用
(2)请求和保持:当一个进程占有一些资源,同时又申请新的资源,若新资源申请失败,进程将占有着资源阻塞等待
(3)不剥夺:进程获得的资源,在未使用完之前,不能被其他进程强行剥夺。
(4)环路等待:发生死锁时,必然存在着“进程--资源”的环形请求链。

处理死锁方法:
(1)预防死锁:设置某些限制条件,破坏死锁产生的必要条件。
(2)避免死锁:也叫“动态预防”,在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免死锁。
(3)检测并解除:允许发生死锁,但可及时检测出来,并精确指明相关进程和资源,同时配合适当措施消除死锁。

操作系统基础知识

线程池
链接
内核基础
操作系统概述
进程管理
内存管理
存储管理
设备管理
文件管理

进程通信

链接
链接2
链接3
链接4

数据库

  • 事务的特性
    图片说明
    图片说明
    图片说明

数据库知识

数据库概念及其原理

数据库引擎

InnoDB:支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。

MyISAM:插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比较低,也可以使用。

MEMORY:所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。

注意,同一个数据库也可以使用多种存储引擎的表。如果一个表要求比较高的事务处理,可以选择InnoDB。这个数据库中可以将查询要求比较高的表选择MyISAM存储。如果该数据库需要一个用于查询的临时表,可以选择MEMORY存储引擎。

并发异常

脏写:脏写是指事务回滚了其他事务对数据项的已提交修改,比如下面这种情况
丢失更新:丢失更新是指事务覆盖了其他事务对数据的已提交修改,导致这些修改好像丢失了一样。
脏读:脏读是指一个事务读取了另一个事务未提交的数据
不可重复读:不可重复读是指一个事务对同一数据的读取结果前后不一致。脏读和不可重复读的区别在于:前者读取的是事务未提交的脏数据,后者读取的是事务已经提交的数据,只不过因为数据被其他事务修改过导致前后两次读取的结果不一样。
幻读:幻读是指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。幻读和不可重复读的区别在于,不可重复读是针对确定的某一行数据而言,而幻读是针对不确定的多行数据。

Select Poll Epoll

  • Select
    默认的最大连接数是1024,64位机器是2048.
    每次扫描socket都是线性扫描,时间复杂度O(n)

  • Poll
    poll() 在应付大数目的文件描述符的时候速度更快,相比于select.
    没有连接数限制,基于链表存储的。

  • Epoll
    epoll的两种工作方式:1.水平触发(LT)2.边缘触发(ET)
    LT模式:若就绪的事件一次没有处理完要做的事件,就会一直去处理。即就会将没有处理完的事件继续放回到就绪队列之中(即那个内核中的链表),一直进行处理。
    ET模式:就绪的事件只能处理一次,若没有处理完会在下次的其它事件就绪时再进行处理。而若以后再也没有就绪的事件,那么剩余的那部分数据也会随之而丢失。
    由此可见:ET模式的效率比LT模式的效率要高很多。只是如果使用ET模式,就要保证每次进行数据处理时,要将其处理完,不能造成数据丢失,这样对编写代码的人要求就比较高。
    注意:ET模式只支持非阻塞的读写:为了保证数据的完整性。

    连接没有限制,支持一个进程打开大数目的socket描述符。
    IO效率不随FD数目增加而线性下降,复杂度O(1)
    没有使用mmap加速内核与用户空间的消息传递

全部评论

相关推荐

不愿透露姓名的神秘牛友
02-12 18:14
RT,这周五就是情人节了,前女友给我发了消息,我该不该回?
Yoswell:原则上来说让她滚,但是本着工作很累下班想吃瓜的心态,我觉得你可以回一下
点赞 评论 收藏
分享
01-07 07:54
已编辑
门头沟学院 前端工程师
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务