面试总结(二)

百度一面:
1、多态、如何实现、虚函数的实现及其作用?哪些可以为虚函数,哪些必须,哪些不可以?虚函数内部是怎么实现的
多态:允许将父对象设置成和一个或多个子对象相等;也就是允许将子类类型的指针赋值给父类类型的指针(一个接口,多种方法,即接口重用);不论传递过来的究竟是哪个类的对象,函数都能够通过同一个借口调用到适应各自对象的实现方法;
C++支持两种多态性:编译时多态性,运行时多态性
a、编译时多态性,也叫静态多态,主要是通过重载函数实现;
b、运行时多态性,也叫动态多态,主要是通过虚函数实现;
运行时多态:声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法;
虚函数:就是允许被其子类重新定义的成员函数,子类重新定义父类虚函数的做法,可实现成员函数的动态覆盖;
作用:实现类的多态性;
纯虚函数:在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法;在基类中实现纯虚函数的方法是在函数原型后加“=0”。
虚函数的内部实现机制:编译器处理虚函数的方法是:为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针,这种数组称为虚函数表;即,每个类使用一个函数表,每个类对象用一个虚表指针;
举例
基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表;
(1)、如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址;
(2)、如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址;注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。
不能声明为虚函数
1)普通函数:它不属于成员函数,是不能被继承的;编译器会在编译时绑定此种函数,而多态体现在运行时绑定,因此它声明为虚函数没有意义;
2)友元函数:它不属于类的成员函数,不能被继承;对于没有继承特性的函数没有虚函数的说法。
3)构造函数:假如子类可以继承基类构造函数,那么子类对象的构造将使用基类的构造函数,而基类构造函数并不知道子类有什么成员,因此不行;另一方面,多态是通过基类指针指向子类对象来实现的,在对象构造之间并没有对象产生,因此无法实现多态特性。因此,构造函数不允许继承;
4)内联成员函数:内联函数是在编译时展开的,而虚函数是在运行时绑定的,因此不可以;
5)静态成员函数:同内联函数,静态成员函数也是在编译时确定,无法动态绑定,因此它也不支持多态;
可以声明为虚函数:
析构函数,普通成员函数

2、逻辑题,1000瓶水,一瓶有毒,用多少个试纸可以检测出有毒的那瓶水?
答;水只有有毒和没毒两种状态,可以看成是比特的0和1;0代表没毒,1代表有毒;
因为2^10=1024>1000,因此只需要10个试纸即可;
将1000瓶水以十位二进制数编号,例如第一瓶水为0000000001,第二瓶水为0000000010,第三瓶水为0000000011,以此类推进行编号;
将二进制数最后位为1的水取出一滴放到一起,用1号试纸去测;
将二进制数倒数第二位是1的水取出一滴放到一起,用2号试纸去测;
以此类推。。。
如果2,3,4,5,6,7,8,9,10号试纸都不变色,只有1号变色的话,说明编号为2的水有毒;
类似于建立一棵层数为10的赫夫曼树,权值为二进制的0或1;遇到0往左走,遇到1往右走;

3、进程间通信,具体实现
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓存区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信
不同进程间的通信本质:进程之间可以看到一份公共资源,而提供这份资源的形式或者提供者不同,照成了通信方式不同,而pipe就是提供这份公共资源的形式的一种;
匿名管道:还有高级管道,有名管道
(1)父进程创建管道,得到两个文件描述符指向管道的两端;
(2)父进程fork出子进程,子进程也有两个文件描述符指向同一管道;
(3)父进程关闭fd[0],子进程关闭fd[1],即父进程关闭管道读端,子进程关闭管道写端(因为管道只支持单向通信);父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入,从读端流出,这样就实现了进程间通信;
信号量:信号量本质上是一个计数器,用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,主要是用来保护共享资源,使得资源在一个时刻只有一个进程独享;
消息队列
消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
共享内存
指两个或多个进程共享一个给定的存储区。
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

1.管道:速度慢,容量有限,只有父子进程能通讯
2.FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

4、TCP/IP 三次握手,具体,协议,包的内容,是否可以传输数据

5、手撕代码最大回文串
思路:遍历每个字符,设置两个指针,一个向前,一个向后,判断是否相等,不相等则返回回文子串的长度,最后求出最大值

//最长回文子串
#include <iostream>
 
using namespace std;
 
//*s为字符串,n为字符串的长度
int LagPalindrome(char *str, int n)
{
	int count = 0;
	int max = 0;//最长回文子串的长度
	if (str == NULL || n<1)
	{
		return 0;
	}
	for (int i = 0; i < n; i++)
	{		
		//子串为奇数时
		for (int j = 0; (i-j)>=0&&(i+j)<n; j++)
		{
			if (str[i - j] != str[i + j])
			{
				break;
			}
			count = 2 * j + 1;
		}
		if (count > max)
		{
			max = count;
		}
		//子串为偶数时
		for (int k = 0; (i - k)>=0 && (i + k + 1) < n; k++)
		{
			if (str[i - k] != str[i + k+1])
			{
				break;
			}
			count=2*k + 2;
		}
		if (count > max)
		{
			max =count ;
		}
	}
	
	return max;
}
 
int main( )
{
	char str[] = "abccba";
	int n = strlen(str);
	int MaxLen;
	MaxLen = LagPalindrome(str, n);
	
	cout << "最长回文子串的长度是:"<<MaxLen<<endl;
 
	return 0;
}

6、new/malloc,delete/free,怎么分配的内存,内存不够怎么办,内存泄漏,后果,怎么检测

猜你喜欢

转载自blog.csdn.net/qq_38224589/article/details/82778939