文章目录
- 1.new/delete malloc/free的区别与联系
- 2.内存泄露有哪些情况
- 3.如何检测到内存泄露
- 4.如何避免内存泄露
- 5.c++有几种智能指针
- 6.shared_ptr是线程安全嘛
- 7.智能指针什么时候会产生内存泄露
- 8.构造函数可以是虚函数嘛
- 9.map是有序的嘛
- 10.map什么时候迭代器会失效
- 11.map插入 删除 查找的时间复杂度
- 12.20亿个数有两个数重复的,如何快速找出。
- 13.基类A 派生类B有几个虚表
- 14.字节对齐的作用
- 15.设计一个聊天服务,在线达到百万级千万级,结合你所学到的知识讲一下,比如难点,或者设计想法。
- 16.设计一个分布式ID的生成方案。
- 17.TCP和UDP的区别
- 18.实现一个可靠的UDP的办法
- 19.MTU(最大传输单元)
- 20.linux 惊群效应
- 21.平时学习知识的途径(书 视频 博客 GitHub)
- 22.遇到难题难点时的解决办法
1.new/delete malloc/free的区别与联系
2.内存泄露有哪些情况
1. 在类的构造函数和析构函数中没有匹配的调用new和delete函数
两种情况下会出现这种内存泄露:一是在堆里创建了对象占用了内存,但是没有显示地释放对象占用的内存;二是在类的构造函数中动态的分配了内存,但是在析构函数中没有释放内存或者没有正确的释放内存
2. 没有正确地清除嵌套的对象指针
3. 在释放对象数组时在delete中没有使用方括号
方括号是告诉编译器这个指针指向的是一个对象数组,同时也告诉编译器正确的对象地址值并调用对象的析构函数,如果没有方括号,那么这个指针就被默认为只指向一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露。如果在方括号中间放了一个比对象数组大小还大的数字,那么编译器就会调用无效对象(内存溢出)的析构函数,会造成堆的奔溃。如果方括号中间的数字值比对象数组的大小小的话,编译器就不能调用足够多个析构函数,结果会造成内存泄露。
释放单个对象、单个基本数据类型的变量或者是基本数据类型的数组不需要大小参数,释放定义了析构函数的对象数组才需要大小参数。
4. 指向对象的指针数组不等同于对象数组
对象数组是指:数组中存放的是对象,只需要delete []p,即可调用对象数组中的每个对象的析构函数释放空间
指向对象的指针数组是指:数组中存放的是指向对象的指针,不仅要释放每个对象的空间,还要释放每个指针的空间,delete []p只是释放了每个指针,但是并没有释放对象的空间,正确的做法,是通过一个循环,将每个对象释放了,然后再把指针释放了。
5. 缺少拷贝构造函数
两次释放相同的内存是一种错误的做法,同时可能会造成堆的奔溃。
按值传递会调用(拷贝)构造函数,引用传递不会调用。
在C++中,如果没有定义拷贝构造函数,那么编译器就会调用默认的拷贝构造函数,会逐个成员拷贝的方式来复制数据成员,如果是以逐个成员拷贝的方式来复制指针被定义为将一个变量的地址赋给另一个变量。这种隐式的指针复制结果就是两个对象拥有指向同一个动态分配的内存空间的指针。当释放第一个对象的时候,它的析构函数就会释放与该对象有关的动态分配的内存空间。而释放第二个对象的时候,它的析构函数会释放相同的内存,这样是错误的。
所以,如果一个类里面有指针成员变量,要么必须显示的写拷贝构造函数和重载赋值运算符,要么禁用拷贝构造函数和重载赋值运算符
C++中构造函数,拷贝构造函数和赋值函数的区别和实现参见:http://www.cnblogs.com/liushui-sky/p/7728902.html
6. 缺少重载赋值运算符
这种问题跟上述问题类似,也是逐个成员拷贝的方式复制对象,如果这个类的大小是可变的,那么结果就是造成内存泄露
7. 关于nonmodifying运算符重载的常见迷思
-
概念:所谓nonmodifying运算符就是不改变操作数的值,并且计算结果是一个与操作同类型的对象的运算符。比如数学运算符:+,-,*,/,%.而关系运算符(bool类型)和赋值运算符(改变左边的操作数)则不是nonmodifying运算符。
-
与nonmodifying运算符有关的主要问题是返回值。为了提高效率节省时间,决定用引用方式来返回运算结果,而不是返回值方式(隐式调用拷贝构造函数)。返回引用相关的临时变量时,会出现内存泄漏问题。(用引用方式返回局部变量是错误的----当函数退出时,与这个引用相关的内存也就退回了栈。从而使函数调用者得到一个指向无效的、没有分配到内存的对象)
解决这一类问题的办法是重载运算符函数的返回值不是类型的引用,而应该是类型的返回值,即不是 int&而是int
8. 没有将基类的析构函数定义为虚函数
当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露
3.如何检测到内存泄露
参考:https://blog.csdn.net/qq_41786318/article/details/81838359
4.如何避免内存泄露
智能指针
5.c++有几种智能指针
shared_ptr unique_ptr auto_ptr weak_ptr
6.shared_ptr是线程安全嘛
shared_ptr线程安全,但他指向的对象不保证安全。
https://blog.csdn.net/D_Guco/article/details/80155323
7.智能指针什么时候会产生内存泄露
当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。
8.构造函数可以是虚函数嘛
不可以。
1.虚函数对应一个虚指针,虚指针其实是存储在对象的内存空间的。如果构造函数是虚的,就需要通过 虚指针执行那个虚函数表(编译期间生成属于类)来调用,可是对象还没有实例化,也就是内存空间还没有,就没有虚指针,所以构造函数不能是虚函数。
2.虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
9.map是有序的嘛
有序。红黑树的实现原理。
10.map什么时候迭代器会失效
对于关联容器(如map, set,multimap,multiset),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可。这是因为map之类的容器,使用了红黑树来实现,插入、删除一个结点不会对其他结点造成影响。erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器。
11.map插入 删除 查找的时间复杂度
O(1) O(1) O(logn)
12.20亿个数有两个数重复的,如何快速找出。
13.基类A 派生类B有几个虚表
虚函数表,字面意思理解就是存放虚函数的表。虚函数表是一个数组,数组元素储存的是虚函数的指针。一个类如果存在虚函数,那么就会有一个虚指针(vptr),这个虚指针指向虚函数表。
而当一个派生类继承了一个有虚函数的基类时,派生类不会产生新的虚表,而是在原来的虚表上更改,自身与基类虚表中相同的函数会覆盖掉虚表中的对应函数,与虚表中不同的虚函数会添加到虚表的尾部。
14.字节对齐的作用
空间换时间。
1、各个硬件平台对存储空间的处理不尽相同,比如一些CPU访问特定的变量必须从特定的地址进行读取,所以在这种架构下就必须进行字节对齐了,要不然读取不到数据或者读取到的数据是错误的。
2、会对CPU的存取效率产生影响:比如有些平台CPU从内存中偶数地址开始读取数据,如果数据起始地址正好为偶数,则1个读取周期就可以读出一个int类型的值,而如果数据其实地址为奇数,那我们就需要2个读取周期读出数据,并对高地址和低地址进行拼凑,这在读取效率上显然已经落后了很多了。
15.设计一个聊天服务,在线达到百万级千万级,结合你所学到的知识讲一下,比如难点,或者设计想法。
16.设计一个分布式ID的生成方案。
比如分配qq号码,需要考虑快速高效,一台服务器无法满足实时高效,多台服务器如何保证不同服务器间生成的号码不重复。
(考虑线程安全 加锁)
17.TCP和UDP的区别
TCP(Transmission Control Protocol,传输控制协议)提供的是面向连接,可靠的字节流服务。即客户和服务器交换数据前,必须现在双方之间建立一个TCP连接,之后才能传输数据。并且提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP(User Data Protocol,用户数据报协议)是一个简单的面向数据报的运输层协议。它不提供可靠性,只是把应用程序传给IP层的数据报发送出去,但是不能保证它们能到达目的地。由于UDP在传输数据报前不用再客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快。
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
18.实现一个可靠的UDP的办法
UDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响。
传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。
1、添加seq/ack机制,确保数据发送到对端
2、添加发送和接收缓冲区,主要是用户超时重传。
3、添加超时重传机制。
详细说明:送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。
目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。
19.MTU(最大传输单元)
MTU = MSS + TCP Header + IP Header.
MTU是网络传输最大报文包。 mss是网络传输数据最大值。
MTU:maximum transmission unit,最大传输单元,由硬件规定,如以太网的MTU为1500字节。
MSS:maximum segment size,最大分节大小,为TCP数据包每次传输的最大数据分段大小,一般由发送端向对端TCP通知对端在每个分节中能发送的最大TCP数据。MSS值为MTU值减去IPv4 Header(20 Byte)和TCP header(20 Byte)得到。
分片:若一IP数据报大小超过相应链路的MTU的时候,IPV4和IPV6都执行分片(fragmentation),各片段到达目的地前通常不会被重组(re-assembling)。IPV4主机对其产生的数据报执行分片,IPV4路由器对其转发的数据也执行分片。然而IPV6只在数据产生的主机执行分片;IPV6路由器对其转发的数据不执行分片。
20.linux 惊群效应
https://blog.csdn.net/lyztyycode/article/details/78648798