学科小知识扩充

1.static 和非static 的区别
局部变量:每次进入子函数都重新赋值,生命周期是子函数结束,内存就会被释放
static局部变量:开始定义时赋值,虽然它的作用域也只是子函数,但是它的生命周期却是整个程序结束。

static函数与普通函数的区别:
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝(堆栈),全局变量存在于(静态区)中,动态申请数据存在于(堆)中。
static全局变量和全局变量的区别:

全局变量的说明之前再加以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。区别在于非静态全局变量的作用域是整个源程序,当源程序有多个源程序构成时,其他源程序也能对非静态全局变量进行访问操作。但是静态全局变量则限制了它的作用域,让它只能在定义它的源程序里访问。

2.程序编译的基本流程
大致分为词法分析,语法分析,语义分析,中间代码生成,代码优化,目标代码生成五个阶段。
词法分析:检查单词合不合法,比如说中间有非法字符的话就不合法。
语法分析:检查程序在结构上合不合法,比如说少了一个分号之类的。知道有自顶向下(逐步推导)或自底向上(逐步归约)的分析方法。
语义分析:检查程序是否符合语言规范,比如说数组的下标是double型的话就会发生语义错误。
中间代码生成:中间代码其实就是一种结构简单,含义明确的记号系统
代码优化:代码优化其实就是在不改变结果的前提下,进行代码的等价交换。它主要在中间代码优化,但是在其他各个阶段也有起作用。
目标代码生成:目标代码可能是机器代码也可能是接近于机器语言的代码。

3.TCP与UDP的区别,TCP如何保证连接的可靠性(三次握手)
速度上:UDP比TCP要快,实时
安全上:TCP比UDP安全,可靠
TCP是面上连接的,,UDP是无连接的。
TCP是面向字节流传输的,有序传输,防止了丢失和差错。UDP是面向报文,尽可能不丢包,不保证顺序。
每一条TCP连接都是点对点的,UDP可以实现一对一,一对多,多对多的连接。
TCP由于头部比UDP的头部要长,所以资源消耗会比UDP多。

TCP的三次握手保证了连接的可靠性:
第一次握手是,客户端发送报文给服务器,等待服务器确定
第二次握手是,服务器接受到了客户端的消息,会回传一条报文给客户端,确认自己收到了报文
第三次握手是,客户端接收到了服务器的消息,然后回传确认的报文给服务器完成了三次握手。
四次挥手对应着断开TCP连接。
完整版:

在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示。

 (1) 第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。

 (2) 第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。

 (3) 第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。



由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。
这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。
收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。
首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。

(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。

(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。

(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

4.操作系统中的进程和线程的区别
1.进程在操作系统中能够独立运行,是资源分配的基本单位,拥有独自的资源,它表示的就是运行中的程序。
2线程是进程中的一个实例,可以说是轻量级的进程,是系统调度和分派的基本单位。
区别在于:
同一个进程中可以有多个线程,一个线程只能对应一个进程。
同一个进程下的所有线程都能共享这个进程的资源。
进程结束后线程就会被销毁。
线程执行时是同步和互斥的,比如说一个线程执行了一部分,可能资源就会被另一个线程占用。

5.设计模式的七大原则

1.单一责任原则
核心:高内聚(模块内部的连接很紧密),低耦合(模块之间尽可能地独立,少关联)
描述:每个类尽可能地实现单一的功能,如果把多个功能放在同一个类中,一个功能修改,可能会终止另外一个功能,所以我们要尽量减少这种情况发生。

2.开-闭原则
核心:对扩展开放,对修改关闭
实现方法:
对不变的功能加以抽象成一个接口
遵循接口的最小功能原则
不足的部分用其他接口实现
模块之间的接口通过抽象接口进行
不断重构

3.里氏替换原则
核心:子类应当可以替代父类并且出现在所有父类可以出现的地方
描述:子类必须实现父类里面的所有方法,在类中调用的其他类必须是接口或者父类,不然就违背了该原则。子类的重载父类的方法时,输入条件必须与父类相等或者更多,这样调用的还是父类的方法。子类允许自己的属性和方法。子类重载父类方法时可以缩小输出结果。

4.依赖倒置原则
核心:要依赖于抽象,不能依赖于具体的实现。
描述:高层模块不能依赖于低层模块,它们应该共同依赖于抽象(抽象类和接口)。抽象不应该依赖于细节,细节(具体实现)应该依赖于抽象。

5.接口隔离原则
核心:一个接口只提供一个对外的功能,不应该把所有的操作都封装在一个接口上。
描述:一个类对另一个类的依赖性应该建立在最小接口上。
分离接口有两种方法:
委托分离接口:把请求委托给别的接口的实现类来完成所需要的职务,这就是适配器模式
多重继承分离接口:通过实现多个接口来实现所需要的职务

6.少继承多组合原则(合成复用)
核心:尽量使用对象组合,而不是通过继承来实现复用。
描述:在一个新的对象里,使用一些旧的对象使它成为新对象的一部分。新对象通过向这些已有对象的委派来复用已有功能的目的。
复用的种类:
继承:优点在于实现起来比较容易,基类的大部分功能都会自动地流入派生类中,派生类也方便扩展或修改。缺点在于这样基类的实现细节就会暴露给派生类,这也被称作白箱操作。这样基类一旦改变就会影响到派生类。
合成聚合:优点在于新对象存取对象成分的唯一方法时通过成分对象的接口。这种复用也被成为黑箱操作,新对象不需要知道成分对象的内部实现,所需要的依赖较小,支持包装,可以动态修改,新对象可以把新的人物委派给合适的对象。

7.最少知识原则(迪米特法则)
核心:类间解耦,低耦合
描述:降低各个类之间的耦合,模块间的通信通过接口来完成,而不用理会类内部的工作原理。
实现方法:
创建低耦合的类,降低类成员的访问权限,尽可能设计为不变类,降低类的访问权限,对其他对象的引用降到最低,不该暴露类成员,应该提供相应的访问器。

6.进程之间的通信方式(IPC):
管道,命名管道FIFO,消息队列,信号量,共享存储,Socket,Streams,其中Socket和Steams是支持两个不同主机的进程通信。
管道:半双工的通信方式,只能单向流动,只允许亲缘进程之间的通信,亲缘进程通常就是父子进程。
命名管道FIFO:半双工的通信方式,但是允许无亲缘关系进程之间的通信。
消息队列:就是消息的链表,存放在内核中,由消息队列标识符标识。
信号量:其实就是计数器,用来控制进程之间对共享资源的访问。
共享内存:多个进程共享同一片内存,实现消息的同步。
Socket:套接字,支持不同机器之间的通信
Steams:只能想到TCP通信。

例子:
使用了【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信。
共享内存用来传递数据;
消息队列用来 在客户端修改了共享内存后 通知服务器读取。
信号量用来同步;

7.A算法
A
是一个启发式搜索算法,从状态空间中对每一个搜索位置进行评估,选出最优的位置,然后再继续评估,一直到终点。
广度搜索算法其实是一种特殊的A算法,也是最烂的A,因为它的估值函数永远是0,没有任何启发性。
选出最小估价,可以用小根堆,在c++中用最小优先队列。
A*算法适合于网格地图。

8.c++虚函数的实现是通过虚表和虚指针,每个类用一个虚表,虚表就记录了基类和派生类中虚函数的内存地址,每个类的对象用一个虚指针。
作用:虚函数是用来实现多态的。声明virtual虚函数,用基类的指针,可以调用派生类的成员。也就是说基类指针可以按照基类的方式做事,也可以按照派生类的方式做事,呈现了多种表现形态,我们称之为多态。
例子:注释的地方都是值得注意的

#include <iostream>
#include <string>
using namespace std;
class People
{
public:
	People(){}//必须有默认的构造参数
	People(string name,int age){
		m_name = name;
		m_age = age;
	}
	virtual void display(){
		cout << m_name << "的年龄为" << m_age<<endl;
	}
protected://声明的成员为保护类型
	string m_name;
	int m_age;
};
class Teacher:public People{//值得注意的是这里的不能漏掉public
public:
	Teacher(string name, int age, int salary){
		m_name = name;
		m_age = age;
		m_salary = salary;
	}
	virtual void display(){
		cout <<m_name<<"的年龄为"<<m_age<<"收入为"<<m_salary<<endl;
	}
private:
	int m_salary;
};
int main(){
	People *p = new People("xiaoming", 67);//声明一个基类指针
	p->display();//调用基类方法
	p = new Teacher("xiaohong", 67, 1000);//把基类指针指向派生类
	p->display();//调用派生类的方法
	system("pause");
	return 0;
}

9.c++的stl容器内部数据结构
vector:数组,元素不够时再重新分配内存,然后把元素拷贝到新数组中
deque(双向队列):数组
list:双向环状链表
slist:单向链表
stack:deque
queue:deque
map,set:红黑树
priority_queue(优先队列,可以做小根堆和大根堆):vector
unordered_map,unordered_set:hash表

10.平衡二叉搜索树
(二叉排序树,二叉搜索树,二叉查找树是同一个东西,左节点小于根节点,右节点大于根节点,键值是不可以重复的。)
定义:一棵空树或者是左右子树高度差的绝对值不超过1的树,并且左右子树也是一棵平衡二叉树,符合平衡条件又符合二叉搜索树条件的叫做平衡二叉搜索树。
红黑树:每个节点都带有颜色属性的二叉搜索树。时间复杂度为log n,空间复杂度为 log n。
b树:b树允许有多个子女,从而减少了树的高度,b树的叶子节点全部出现在同意层,而且都是没有关键字信息的,是一种多路平衡搜索树。
b+树:与b树不同的是,b+树的叶子节点包含了所有关键字信息,以及指向关键字信息的指针,然后叶子节点本身是按照关键字信息从小到大连接起来的,所有的非终端节点都可以当作索引部分。

11.建堆的过程(以大根堆为例)
先用所有元素来构建出一个完全二叉树,然后从最后一个非叶子节点开始调整。
先比较根,左孩子,如果左孩子比根大,则交换,交换后的两个节点都必须再次满足根节点最大的要求,否则又必须调整。
然后再比较右孩子,如果右孩子比根大,则交换,交换后的两个节点都必须再次满足根节点最大的要求,否则又必须调整。
直到树的根节点调整完就结束。

12.AB打包流程
http://www.360doc.com/content/17/0609/21/40005136_661467159.shtml
代码打包
https://blog.csdn.net/qq_18995513/article/details/51990523
Unity—AssetBundle的打包及四种加载资源方式
http://blog.sina.com.cn/s/blog_140bb6bd40102xajb.html
ab原理
http://www.xuanyusong.com/archives/2373

13.NGUI和UGUI的区别(后续补充)
NGUI是第三方开发的UI插件,而UGUI是unity内部的插件。
NGUI有自己的代码框架,而UGUI与平常的使用更接近。
NGUI有自己的图集需要维护,而UGUI则需要将图片类型改为Sprite(2D and UI),最后才会合成图集。
当UI需要与鼠标交互时,NGUI需要绑定colliders,但是UGUI不用。
NGUI支持图文混排,但是UGUI不行。
UGUI发展很快,并且可以实现更多的功能,可以取代NGUI原有的地位。

14.图论
1.在图中常常将数据元素称为顶点。
2.图是由顶点的有穷非空集合和顶点之间的边集合组成。
3.图中任意两个顶点之间的边是无向边,则该图为无向图,否则该图就是有向图。
4.简单图:不存在到自身的边,而且同一条边不能重复出现。
5.邻接点:有边的两个点叫做邻接点,同时称这条边依附于这两个点。有向图中,箭头指向的点可以称为另一个点的邻接点。
6.有向边称为弧。
7.无向完全图:任意两个顶点之间都存在边。含有n个顶点的无向完全图有n*(n-1)/2条边。
有向完全图:任意两个顶点之间都存在方向互为相反的两条弧。含有n个顶点的有向完全图有n*(n-1)。
8.顶点的度:依附该顶点的边的个数。
在n个顶点,e条边的无向图中,度的个数为2e,入度和出度相等。
在n个顶点,e条弧的有向图中,该顶点为弧头的弧的个数为入度的个数,该顶点为弧尾的弧的个数为出度的个数。
9.在图中,权通常是指对边赋有意义的数值量。边上带有权的图叫做网。
10.路径上边的数目成为路径长度,第一个顶点和最后一个顶点相同的路径成为回路或者环。
11.简单路径:顶点不重复出现的路径。简单回路:除了第一个和最后一个顶点外,其他顶点不重复出现的回路叫做简单回路。
12.连通图:在无向图中,任意两个顶点之间都有路径(注意不是边)的图。
非连通图中的极大连通子图称为连通分量(即有多少个独立连通的小部分)。
13.强连通图:在有向图中,任意两个顶点之间都有路径,则称该有向图为强连通图。
非强连通图中的极大连通子图称为强连通分量。
14.深度优先遍历:访问顶点v,把v入栈,从v中未访问的邻接点中选择一个顶点w,把w入栈,再对w进行深度搜索,重复步骤,当一个顶点再也没有未访问的邻接点时则弹出栈,直到栈为空则停止。
广度优先遍历:访问顶点v,把v入队列,访问v的所有邻接点,把它们依次入队列,当一个队头顶点再也没有未访问的邻接点时就弹出队列,直到队列为空为止。
15.邻接矩阵:图的邻接矩阵储存也称为图的数组表示法,用一个一维数组储存顶点,然后用一个二维数组储存边的信息(各顶点间的邻接关系),显然无向图的邻接矩阵一定是对称矩阵。
16.邻接矩阵解决了的问题:
在无向图中,顶点i的度等于邻接矩阵的第i行或者第i列非零元素的个数。
在有向图中,顶点i的出度等于邻接矩阵第i行非零元素的个数。
要判断两个顶点是否有边,只需要判断数组元素值是否为1即可。
邻接矩阵和邻接表是图的两个常用的存储方法。
17.最小生成树:生成树上个边的权值之和称为生成树的代价,代价最小的生成树称为最小生成树。
最小生成树必须包括最小权值的边。
18.求最短路径算法:dijkstra算法(贪心算法的例子,每个最短路径相加就成为了最终的最短路径)和floyd算法(动态规划)
dijkstra算法时间复杂度为:O(n^2),floyd算法时间复杂度为: O(n^3)。
19.用顶点表示活动,用弧来表示活动之间的优先关系,这样的有向图被称为AOV网
AOV网中没有入度的点叫做源点,没有出度的点叫做终点。
AOV网的两个性质:
只有进入该顶点的各活动都已经结束,该顶点所代表的事件才会发生。
只有在该顶点事件发生后,从该顶点出发的各活动才能开始。
具有最大路径长度的路径叫做关键路径,关键路径上的活动称为关键活动。
关键活动长度是整个工程所需要的最短工期,要缩短工期就必须加快关键活动。

15.UNET
UNET是unity自带的网络传输和管理集成组件,可以实现多人游戏的同步更新。
https://blog.csdn.net/qq_25601345/article/details/78551567?locationNum=8&fps=1

16.哈希表
原理:用一个下标范围比较大的数组来进行储存。然后设计一个散列函数,让每一个元素关键字对应一个散列值。但是一个散列值很可能会对应着多个元素关键字,这时候就需要比较函数进行比较。尽管是这样,hash表也大大地提高了存储和查找的时间,而代价只是消耗了更多的内存。
在c++中,hash表并不是标准的stl。

17.银行家算法
银行家算法是一种避免死锁的经典方法,它会事先判断进程所需要的资源会不会影响到整个系统的安全,如果安全则分配,不安全则不分配(不安全就是有可能发生死锁)。

18.mvc架构(model,view,controller)
mvc架构在unity的应用:mvc架构非常适合于UI的开发,UI界面相当于view,UI转换控制相当于controller,UI上的数据变换相当于model。

19.unity常用函数
https://blog.csdn.net/nobcaup/article/details/51502852

20.知道前序中序求后序,知道后序中序求前序。
例子:
原有序列:abcdef
前序:abdecf
中序:dbeafc
后序:debfca
怎么求后序呢,先从前序中得出a为根,然后a就把中序分成了两半,dbe为左子树的中序遍历,fc为右子树的中序遍历,但我们还不知道它们的顺序。同时我们可以从前序中找出这两半,bde为左子树的前序遍历,cf为右子树的前序遍历。
如果只看中序遍历我们是看不出来的原序列的,特别是大量数据的时候。
所以我们必须结合前序来进行判断,这样又变成了一个子问题,如果用编程来实现的话更方便一些。
但是如果是选择题的话,我们必须自己找出来,首先从简单的右子树开始,前序cf,中序fc,这就说明了c是根,f为左孩子节点。
然后再到左子树前序bde,中序dbe,容易看出b为根节点,d为左孩子节点,e为右孩子节点。
如果数据比较多的时候我们就不能一眼看出了,我们必须按照一开始的方法划分。知道了原有序列,得出后序就很简单了。
如果知道后序和中序也是一样的。
但是知道前序和后序该是如何呢,这个我百度过,没有得出结论,但是如果是选择题的话,我们可以使用目测法。(但不保证数据多的时候能用)
前序:abdecf
后序:debfca
仔细观察,除去a以外,bde和deb的位置是相互变动的,然后,cf和fc也是相互变动的,(变动的位置不会超出它们的范围则把它们分为一组)我们可以把它们分开。
然后就能逐步推断出原序列了。

21.new、delete、malloc、free关系
new和delete对应,是c++的运算符。malloc和free对应,是c&c++的标准库函数,它们都有动态申请内存和清理内存的作用。但是对于非内部对象时,对象创建的同时要自动执行构造函数,在对象销毁之前要自动调用析构函数。由于malloc和free是库函数,我们不能把调用构造函数和析构函数的任务强加与它们,所以c++出现了两个新的运算符new/delete来执行这个任务。

22.delete与 delete []区别
delete与new配套,delete []与new []配套,delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。

23.基类和子类的构造函数和析构函数关系:
定义一个对象时先调用的是父类的构造函数,再调用子类的构造函数。
销毁一个对象时先调用的时子类的析构函数,再调用父类的析构函数。

24.多态:对于不同的对象接受相同的信息有不同的活动。
多态性:程序的运行时的多态性是通过继承和虚函数来来体现。
虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。
纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。
纯虚函数的定义:virtual <类型><函数名>(<参数表>)=0;
从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类要声明为抽象类(abstract class)。
抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。
(重载与多态无关)

25.什么是“引用”?申明和使用“引用”要注意哪些问题?
引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

26.将“引用”作为函数参数有哪些特点?
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

27.在什么时候需要使用“常引用”? 
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;

例1

int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确

例2
string foo( );
void bar(string & s);
那么下面的表达式将是非法的:
bar(foo( ));
bar(“hello world”);
原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。

28.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?(转发,有待研究)

格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }

好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!

注意事项:
(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)流操作符重载返回值申明为“引用”的作用:
流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。
赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

#include<iostream.h>

int &put(int n);

int vals[10];

int error=-1;

void main()

{

put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10; 

put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20; 

cout<<vals[0]; 

cout<<vals[9];

} 

int &put(int n)

{

if (n>=0 && n<=9 ) return vals[n]; 

else { cout<<"subscript error"; return error; }

}

(5)在另外的一些操作符中,却千万不能返回引用:±*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

29.结构体与联合有和区别?
(1). 结构体和联合都是由多个不同的数据类型成员组成, 联合中所有成员共用一块地址空间的, 而结构体的不同成员的存放地址不同。
(2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构中的不同成员赋值是互不影响的。

30.重定义redefining
  派生类对基类的成员函数重新定义,即派生类定义了某个函数,该函数的名字与基类中函数名字一样。
  重定义也叫做隐藏,子类重定义父类中有相同名称的非虚函数(参数可以不同)。如果一个类,存在和父类相同的函数,那么这个类将会覆盖其父类的方法,除非你在调用的时候,强制转换为父类类型,否则试图对子类和父类做类似重载的调用时不能成功的。
重定义需要注意:
不在同一个作用域(分别位于基类、派生类)
函数的名字必须相同
对函数的返回值、形参列表无要求
若派生类定义该函数与基类的成员函数完全一样(返回值、形参列表均相同),且基类的该函数为virtual,则属于派生类重写基类的虚函数
若重新定义了基类中的一个重载函数,则在派生类中,基类中该名字函数(即其他所有重载版本)都会被自动隐藏,包括同名的虚函数

31.C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。
main 函数执行以前,还会执行什么代码?
答案:全局对象的构造函数会在main 函数之前执行。

32.描述内存分配方式以及它们的区别?(特别注意)
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。(记住静态就是在静态存储区的,不管是局部还是全局)

2) 在栈上创建。在执行函数时,函数内局部变量都是在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。

BOOL : if ( !a ) or if(a)

int : if ( a == 0)

float::if ( a < 0.000001 && a >-0.000001)

pointer : if ( a != NULL) or if(a == NULL)

33.虚继承的作用:虚继承:多个派生类保存相同基类的同名成员时,虽可以在不同的数据成员中分别存放不同的数据 ,但我们只需要相同的一份。解决了多父类重复成员只保留一份的问题。
举个例子,有一张沙发床。它继承于沙发类,也继承于床类。
但是它们都继承于家具类,实质上我们只需要一个家具类属性(包含长宽高),但是如果分别继承则会产生两个不同的属性,这时我们就必须用虚继承了。
当床和沙发都虚继承于家具类时,它们便公用了同一个家具属性。
子类沙发床继承于沙发和床也是共用这套属性。

34.请说出const与#define 相比,有何优点?
const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。(#define没有安全检查,无类型,知识字符替换)
2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

35.int (*s[10])(int) 表示的是什么?
int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。

36.引用与指针有什么区别?

  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。

37.基类的析构函数不是虚函数,会带来什么问题?
派生类的析构函数用不上,会造成资源的泄漏。

38.构造函数不能声明为虚函数

  1. 对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
    答 、c用宏定义,c++用inline

40.函数指针和指针函数的区别?
函数指针是指向一个函数入口的指针;
指针函数是函数的返回值是一个指针类型。

41.指针的几种典型应用情况?

答:
int *p[n];—–指针数组,每个元素均为指向整型数据的指针。

int (*)p[n];—p为指向一维数组的指针,这个一维数组有n个整型数据。

int *p();——函数带回指针,指针指向返回的值。

int (*)p();—-p为指向函数的指针。

42.unordered_map,unordered_set,map和set的用法和区别
unordered_map和unordered_set内部是无序的,它们是基于hash表的,map和set是有序的,基于红黑树。
unordered_set和set可以用于存储无重复数,只可以通过迭代器访问
unordered_map和map用来存储键值对,一一对应,可以通过下标访问。
都有find方法,得到的对象是一个iterator。
set用insert方法进行插入,map可以用下标进行插入,也可以通过insert方法来进行访问mymap.insert(make_pair(“c++”,100))。
都用erase方法来删除数据。

43.const的一些用法:
const是C语言的一种关键字,起受保护,防止以外的变动的作用!可以修饰变量,参数,返回值,甚至函数体。const可以提高程序的健壮性,你只管用到你想用的任何地方。
注意: 如果用const来修饰成员函数的话,是不能修改成员变量的。
如果参数或者返回值是值类型的话,使用const没有意义。
http://www.cppblog.com/xczhang/archive/2008/01/13/41092.html

const和define的区别:

  1. 编译器处理方式不同
      define宏是在预处理阶段展开。
      const常量是编译运行阶段使用。
    (2) 类型和安全检查不同
      define宏没有类型,不做任何类型检查,仅仅是展开,可能会有未知错误。
      const常量有具体的类型,在编译阶段会执行类型检查。
    (3) 存储方式不同
      define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
      const常量会在内存中分配(可以是堆中也可以是栈中)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
字典是一种用于收集、整理和解释词汇的工具。它是学科和专业领域中非常重要的资源之一。学科专业词汇字典是包含特定学科或专业领域中术语、定义、解释和相关信息的书籍或在线资源。 学科专业词汇字典的作用是帮助学习者和从业者理解和掌握该领域中的专业术语。通过字典,人们可以查找和学习不熟悉的词汇、了解其含义和用法,从而提高对学科和专业知识的理解。 学科专业词汇字典可以分为通用性和专业性两种。通用性的学科专业词汇字典适用于广泛的学科和专业领域,如医学、经济学、法律等。而专业性的学科专业词汇字典则专注于某个特定的学科或专业领域,如计算机科学、化学工程等。 字典中通常包含词汇的拼写、音标、词义、用法及例句等信息。一些更全面的字典还会提供词汇的词源、同义词和反义词、搭配用法等额外信息。这些信息帮助人们更好地理解和应用这些词汇。 另外,随着科技的发展,许多学科专业词汇字典已经转移到了在线平台上。这些在线资源不仅提供了更加方便快捷的查询功能,还可以不断更新和扩充词汇内容,使其始终保持最新的信息。 总之,学科专业词汇字典是学习和从事学科和专业工作的重要工具。它们帮助人们理解和应用学科和专业术语,提高专业知识水平,同时也为学术交流和专业交流提供了基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值