1、指针和引用的区别:
概念:指针是一个变量,这个变量是用来存储地址的,指向内存的一个存储单元;
引用是原变量的一个别名,对于引用的变量修改也会对原变量修改。
区别:1)引用不可以为空,引用创建时就必须初始化;指针可以是空值,可以任何时候初始化,
2)指针可以有多级指针,但是引用只有一级.
3)指针和引用自增的意义不一样
2、C语言和C++的区别:
C语言是过程化编程:所谓过程化编程就是程序只有顺序、分支和循环三种结构,但是C语言又具有goto关键字。
C++是面向对象语言:具有面向对象的三个特征
区别(大方向--开发时的运用方向):c语言主要解决的问题是,不同机器平台上的汇编语言指令,功能相似但写法不一样。
c++是为了解决操作系统上的API功能相似但是形式不同的问题;这些功能相似的代码很难用同一种形式的C语言表达出来(所以要有面向对象和模版)
从语言层面:1)数据的输入输出(c和c++的引入头文件不同,输入输出的函数不同)
2)传值,传参
3)新增的几种函数(内联函数、友元函数、虚函数、构造、析构函数)
4)命名空间
5)动态内存分配和释放(c是malloc函数分配,free释放;c++是new分配,delete释放)
3、c++作用域:
概念:一种语言最基础的部分之一就是变量,而变量非常重要的特征就是作用域和生存周期,作用域即是指变量在代码中的有效区域
作用域分类:1)函数原型作用域:函数声明参数从参数声明到函数声明结束
2)局部作用域:函数作用域,在函数中声明之后到函数执行完为其声明周期,变量的使用范围是局限的,只在函数内起作用
3)类作用域:分为三种类型(public、protect、private)protect可以被自己类和继承类调用。
4)命名空间作用域:可分为三类:显示、全局明明空间(using namespace std;)、匿名命名空间
引伸:命名空间的作用的为了避免歧义,给定范围(例如在初二级A、B两个班都有一个叫小红的女生,你在走廊想叫A班的小红,但是回答你的会有两个,所以我们要说明是A班的小红,这样B班的小红就不会回应。)
4、内存管理机制:
(一)内存管理机制:
程序内存区域划分:
由高位到低位:1、栈:由编译器自动分配和释放(存放局部变量、函数参数等)
2、堆:一般由程序员分配和释放,若程序员不释放内存,程序结束后操作系统会自动回收(动态内存分配)
//3、自由存储区:用于malloc分配的空间,和堆十分相似,不过要用free释放空间
4、数据段:全局变量/静态存储区,全局变量和静态变量分配到同一块内存中
5、常量存储区域(专门的地方,程序结束后释放)
6、代码段(存放为程序代码)
引伸问题:堆栈对比:
(1)管理方式不同:栈是系统进行分配和回收,堆是程序员自动分配和回收
(2)空间大小不同:
(3)能否产生碎片不同:对于堆来说,频繁的new/delete操作会造成内存空间不连续,从而造成大量的碎片,使程序效率降低
(4)生长方向不同:对于堆来说生长方向是向上的,向着内存地址增加的方向;栈是由高向低增长的
(5)分配方式不同:堆都是动态分配的,栈有动/静态两种分配方式。静态分配由编译器完成,比如局部变量的分配。动态分配由alloc函数执行,但是栈的动态分配和堆的动态分配是不同的,它的动态分配是由系统释放不用手动操作
(6)分配效率不同:栈比堆快,栈是系统提供的数据结构,分配专门寄存器存放栈,压栈出栈有专门的指令操作;而对是要调用c++函数来进行分配和释放,库函数还要按照移动的算法进行分配。
(二)内存泄漏机制:
几种内存泄漏:(1)常发性内存泄漏(发生内存泄漏的代码被多次执行)
(2)偶发性内存泄漏(当调用到会发生内存泄漏的代码是才会发生)
(3)一次性内存泄漏 (该内存泄漏代码只被执行一次)
(4)隐形内存泄漏 (有完整的内存分配和内存释放机制,但是要在运行很久或很多次之后再进行内存释放,会造成内存耗尽)
内存泄漏检查:关键就在于能截获住对分配内存和释放内存的函数调用,这样我们就可以跟踪一块内存周期
(三)内存回收机制:垃圾回收的算法的基础通常基于扫描并标记当前可能被使用的所有内存块,从已经被分配的所有内存中把未标记的内存回收来做的。C/C++ 中无法实现垃圾回收的观点通常基于无法正确扫描出所有可能还会被使用的内存块,但是,看似不可能的事情实际上实现起来却并不复杂。首先,通过扫描内存的数据,指向堆上动态分配出来内存的指针是很容易被识别出来的,如果有识别错误,也只能是把一些不是指针的数据当成指针,而不会把指针当成非指针数据。这样,回收垃圾的过程只会漏回收掉而不会错误的把不应该回收的内存清理。其次,如果回溯所有内存块被引用的根,只可能存在于全局变量和当前的栈内,而全局变量(包括函数内的静态变量)都是集中存在于bss 段或 data段中
5、malloc和new的区别:
1、属性:new/delete是c++guanjianzi,需要编译器支持,malloc/free是库函数,需要头文件支持
2、参数:使用new操作符进行内存分配不需要制定内存大小,编译器乎根据类型信息自行计算;malloc需要显示地支出所需要的内存大小
3、返回类型:new分配内存成功时,返回与对象类型相同的指针,而malloc返回的是void*,需要转换成我们需要的类型
4、new内存分配内存失败会抛出bac_alloc异常,malloc失败返回NULL
5、c++允许重载new/delete操作符,new就不需要为对象分配内存,而是制定一个地址作为内存起始区,new在这段内存上调用构造函数完成初始化工作,malloc是不能重载的
6、智能指针:
实质:智能指针是用了RAll(资源获取即初始化)的技术对普通指针进行了封装 。这一层封装的机制是为了能够使只能指针更好的管理一个对象的生命周期(防止程序员忘记了delete),在foo()运行时抛出了异常。那么temp_ptr所指向的对象任然不会被安全删除
这就会产生野指针,对于指针指向的内容我们就无法把控,甚至造成内存泄漏。(实质是存放在栈的模板对象,表现的像指针,在栈的声明结束时,指针指向的堆内存自然也被释放,从而实现只能管理)
引伸:RAll技术是c++之父(本杰尼)提出的,它的设计理念是把资源和对象的生命周期绑定,对象创建时获取资源,对象销毁时释放资源。
7、vector和list容器:(vector有两个函数一个是capacity<返回对象缓冲区>和size<返回当前对象缓冲区数据存储的个数>)
1)vectoe是顺序表,表示的是一块连续的内存,元素被顺存储,list是双向连接表,在内存中不一定连续。
2)内存开销不同:vector会在当前内存不够时再开辟出一块新的足够大的空间,把原来的数据拷贝到新的空间中;而list不考虑内存连续问题,所以内存开销比vector小
3)list只能通过指针访问元素,随机访问元素的效率很低,在进行大量的随机存取元素时,使用vector更加合适
4)vector插入或者删除一个元素时需要复制移动带插入元素右边的所有元素,因此在有频繁插入删除操作时,使用list更加的合适
8、内联函数和友元函数:
内联函数使用特点:1)一个函数被不断重复调用
2)函数只有简单的几行,目标函数不包含for、while、seitch语句
内敛函数目的 :加快程序运行速度,因为内敛函数不需要中断调用,在编译的时候内敛函数等于是直接镶嵌到目标函数中
友元函数目的:1)为了在该类中提供一个对外访问接口
2)友元函数不属于类成员,他是定义在类外的普通函数,只是在勒种声明了该函数,该函数就可以访问类中的private和protect成员
9、内联函数和宏定义的区别:
1)在预编译时,宏定义通过预处理器进行字符串的原样替换,而内敛函数是通过编译器来控制实现的
2)内联函数是真正的函数,在使用时像宏定义一样展开,取消了函数参数的压栈,减少调用开销。
3)编译器会对内联函数的参数类型做安全处理,宏定义不会
4)内敛函数可以访问类的成员变量,宏定义不能
5)在类中声明同时定义的成员函数自动转化为内联函数
10、函数重载、覆盖和隐藏
1)成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
2)覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
3)“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类
的函数被隐藏
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。
此时基类的函数被隐藏
总结:(1)成员函数被重载,是在一个类当中,具体的概念上面已经有所陈述。
(2)覆盖指派生类函数覆盖基类函数,从其特征可以看出,它是为了实现多态的效果。描述了要实现多态的必要条件
(3)“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,其特征实际上是要实现多态的效果所需要的充要条件。
11、虚函数和纯虚函数(虚函数是c++多态的一种表现)
虚函数的作用:实现动态联编,在程序运行阶段动态的选择合适的成员函数,在定义了虚函数之后可以对基类的派生类进行虚函数的重新定义,派生类的函数要和基类函数有相同函数名和参数类型以及参数个数(重写)。
目的:定义为虚函数是为了允许基类的指针调用子类的这个函数
纯虚函数(有纯虚函数的类等同于抽象类,是不可以进行实例化的,只能用派生出去的子类进行实例化):
1)为了制定标准,为了使程序更加通用化,提高程序的重用性,让所有实现或者继承它的子类全部按照统一标准工作。
2)因为要实现基类中的方法所以只能通过派生类的实例化去实现,编译器要求派生类必须重写纯虚函数进行重写,这样就可以实现多态
引伸:要实现动态联编的三个条件:
1)必须把动态两边的行为定义为虚函数
2)类之间存在子类型关系,一般表现为一个类从另一个类共有派生而来
3)必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数
12、多态(动态多态和静态多态):
编译时多态(静态多态):通过重载函数实现
运行时多态(动态多态):通过虚函数实现
概括:c++多态概括起来就是在基类函数中声明函数为虚函数,而在派生类中再对虚函数进行重写,程序运行时实例化的对象就会根据实际类型来调用相应的函数(基类的或者派生类的),以此来实现多态(虚函数表存在于实例的内存中)
1)用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
2)存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
3)多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性。
4)多态用虚函数来实现,结合动态绑定.
5)纯虚函数是虚函数再加上 = 0;
6)抽象类是指包括至少一个纯虚函数的类。
13、STL标准模板库
概念:是标准库的一部分
STL六大组件:容器(containers):map 、list 、vector;map是stl的关联容器,他提供一对一的数据处理,其中的数据是以键值对的形式存在的(key,value),map内部自成一颗红黑树(一种非常严格意义上的平衡二叉树),这棵树具有对数据自动排序的功能,所以map里面的数据是有序的,建值在map中不可以重复。
算法(algorithms):提供各种找那个算法函数和模块,比如在容器中做排序用sort()函数,就是实现的内部算法模块
迭代器(iterators):指向容器中数据的指针
函数对象(functors):
适配器(adapters):
分配器:
14、几种关键字
1)const:
(1)修饰指针,防止指针被修改
(2)修饰普通类型,说明这个参数不应该修改
(3)修饰引用类型,参数的值不能被修改,也就失去了引用类型的效果,但传递对象时,可以起到不copy对象的目的
(4)用const修饰函数说明函数不会修改成员变量的值
2)static:
(1)第一个是作用域,第二个是保持变量内容持久化
静态全局变量:
该变量在全局数据区分配内存;
未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化);
静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;
3、面向对象的三个特征