C++面试题五---散碎问题

1、malloc和new的区别
1)malloc与free是C++/c语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存;
2)new 返回指定类型的指针,并且可以自动计算所需要大小。而 malloc 则必须要由程序员计算字节数,并且在返回后强行转换为实际类型的指针;
3)new/delete在对象创建的同时可以自动执行构造函数初始化,在对象在消亡之前会自动执行析构函数。而malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的;
既然new/delete的功能覆盖了malloc/free,为什么C++还要保留malloc/free?因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。

2、 hash 冲突及解决办法
关键字值不同的元素可能会映象到哈希表的同一地址上就会发生哈希冲突。解决办法:
1)开放定址法:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探查到开放的 地址则表明表中无待查的关键字,即查找失败。
2) 再哈希法:同时构造多个不同的哈希函数。
3)链地址法:将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
4)建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

3、 class和struct的区别
https://blog.csdn.net/yuliu0552/article/details/6717915
C++中的struct对C中的struct进行了补充,它已经不再只是一个不同数据类型的数据结构,struct能包含成员函数,struct能继承,struct能实现多态。
既然这些它都能实现,那么它和class还能有什么区别?
最本质的一个区别就是成员默认属性和默认继承权限的不同:
若不指明,struct成员的默认属性是public的,class成员的默认属性时private的;
若不指明,struct成员的默认继承权限是public的,class成员的默认继承权限是private的;
当然,到底默认是public继承还是private继承,取决于子类而不是基类。
我的意思是,struct可以继承class,同样class也可以继承struct,那么默认的继承访问权限是看子类到底是用的struct还是class。
还有一个区别是:class这个关键字还用于定义模板参数,就像“typename”,但关键字struct不用于定义模板参数。
做个总结,从上面的区别,我们可以看出,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。

4、 请用简单的语言告诉我C++是什么?
答:C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛。C++支持多种编程范式--面向对象编程、泛型编程和过程化编程。其编程领域众广,常用于系统开发,引擎开发等应用领域,是最受广大程序员受用的最强大编程语言之一,支持类:类、封装、继承、多态、重载等特性!

5、 C和C++的区别?
C++是C的扩展,是在C的基础上添加类,C是结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入进行运算处理得到输出;而对于C++,首先要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现对事件控制。

6、 什么是面向对象思想(OOP)
https://blog.csdn.net/chenqiuping_ls/article/details/53609807
面向对象是一种思想,是相对于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想是将数据作为第一位,而方法或者说是算法作为其次,这是对数据一种优化,操作起来更加的方便,简化了过程。面向对象有三大特征:封装性、继承性、多态性,其中封装性指的是隐藏了对象的属性和实现细节,仅对外提供公共的访问方式,这样就隔离了具体的变化,便于使用,提高了复用性和安全性。对于继承性,就是两种事物间存在着一定的所属关系,那么继承的类就可以从被继承的类中获得一些属性和方法;这就提高了代码的复用性。继承是作为多态的前提的。多态是说父类或接口的引用指向了子类对象,这就提高了程序的扩展性,也就是说只要实现或继承了同一个接口或类,那么就可以使用父类中相应的方法,提高程序扩展性,但是多态有一点不好之处在于:父类引用不能访问子类中的成员。

7、 设计模式懂嘛,简单举个例子?
设计模式是一套被反复使用,无数人知晓的,经过分类目的的,代码设计经验的总结
比如单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用于:当类只能有一个实例而且可以从一个众所周知的访问点访问它时;当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
比如工厂模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
适用于:当一个类不知道它所必须创建的对象的类的时候;当一个类希望由它的子类来指定它所创建的对象的时候;当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

8、 STL库用过吗?常见的STL容器有哪些?算法用过哪几个?
STL主要包含容器、算法、迭代器三部分(还有仿函数、空间配置器、配接器)
容器是存放数据的地方,在STL中,容器主要分为两类:序列式容器和关联式容器。
序列式容器,其中元素不一定有序,但都可以被排序,如:vector,list,stack,deque…
关联式容器,内部结构基本是一颗平衡二叉树,每个元素都有一个键值和一个实值,元素按照一定的规则存放。如:set,map,multiset,multimap…
算法有:排序、合并、复制、查找、遍历操作、比较、交换、修改、移除、反转…

9、 const 的作用
https://blog.csdn.net/Eric_Jo/article/details/4138548
const定义常量,修饰函数的参数和函数的返回值,修饰类的成员函数
const修饰类的成员变量,表示成员常量,不能被修改
const修饰函数保证在本函数内部不会修改类内的数据成员,不会调用其它非const成员函数
const函数只能调用const函数,非const函数可以调用const函数
类外定义的const成员函数,在定义和声明出都要const修饰符
如果 const 构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数。

10、 关键字static的作用
https://blog.csdn.net/yuechuxuan/article/details/81698776
(1)函数体内的static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍然维持定义值
(2)模块内的static全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问
(3)在模块内的static函数只被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内
(4)在类的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量

11、 类的static变量在什么时候初始化?函数的static变量在什么时候初始化?
类的静态成员变量在类实例化之前就已经存在了,并且分配了内存。函数的static变量在执行此函数时进行初始化。

12、什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?
用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄漏
避免方法:使用的时候要记得指针的长度;malloc/new的时候要有对应的free/delete;对指针赋值的时候应该注意被赋值指针需要不需要释放;动态分配内存的指针最好不要再次赋值。

13、 在函数内定义一个字符数组,用gets函数输入字符串的时候,如果输入越界,为什么程序会崩溃?
答:因为gets无法截断数组越界部分,会将所有输入都写入内存,这样越界部分就可能覆盖其他内容,造成程序崩溃。

14、 解释C++中静态函数和静态变量?
(1)类静态数据成员在编译时创建并初始化:在该类的任何对象建立之前就存在,不属于任何对象,而非静态类成员变量则是属于对象所有的。类静态数据成员只有一个拷贝,为所有此类的对象所共享。
(2)类静态成员函数属于整个类,不属于某个对象,由该类所有对象共享。
static 成员变量实现了同类对象间信息共享。
static 成员类外存储,求类大小,并不包含在内。
static 成员是命名空间属于类的全局变量。
static 成员只能类外初始化。
可以通过类名访问(无对象生成时亦可),也可以通过对象访问。
静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。
静态成员函数只能访问静态数据成员。原因:非静态成员函数,在调用时 this指针时被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。

15、 TCP和UDP的区别
https://blog.csdn.net/yulyu/article/details/69062288
TCP提供的是面向连接的、可靠的数据流传输,而UDP提供的是非面向连接的、不可靠的数据流传输。简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性一般。
TCP特点:建立连接通道;数据大小无限制;速度慢,但是可靠性高
UDP特点:把数据打包;数据大小有限制(64K),不建立连接;速度快,但可靠性低
举例说明:
TCP就像打电话,两者之间必须有一条不间断的通路,数据不到达对方,对方就一直在等待,除非对方先挂电话。先说的话先到,后说的话后到,有顺序。
UDP就像寄一封信,发信者只管发,不管到。但是你的信封上必须写明对方的地址,发信者和收信者之间没有通路,靠邮电局联系。信发到时可能已经过了很久,也可能根本没有发到。先发的信未必先到,后发的也未必后到。

16、 三次握手和四次挥手过程
https://blog.csdn.net/li0978/article/details/52598121
三次握手建立连接阐述:
第一次握手:客户端要和服务端进行通信,首先要告知服务端一声,遂发出一个SYN=1的连接请求信号,”服务端哥哥,我想给你说说话”。
第二次握手:当服务端接收到客户端的连接请求,此时要给客户端一个确认信息,”我知道了(ACK),我这边已经准备好了,你现在能连吗(SYN)”。
第三次握手:当客户端收到了服务端的确认连接信息后,要礼貌的告知一下服务端,“好的,咱们开始联通吧(ACK)”。
到此整个建立连接的过程已经结束,接下来就是双方你一句我一句甚至同时交流传递信息的过程了。

四次挥手断开连接阐述:
第一次挥手:双方交流的差不多了,此时客户端也已经结尾了,接下来要断开通信连接,所以告诉服务端“我说完了(FIN)”,此时自身形成等待结束连接的状态。
第二次挥手:服务端知道客户端已经没话说了,服务端此时还有两句心里话要给客户端说,“我知道你说完了(ACK),我再给你说两句,&*……%¥”。
第三次挥手:此时客户端洗耳恭听继续处于等待结束的状态,服务器端也说完了,自身此时处于等待关闭连接的状态,并对告诉客户端,“我说完了,咱们断了吧(FIN)”。
第四次挥手:客户端收知道服务端也说完了,也要告诉服务端一声(ACK),因为连接和断开要双方都按下关闭操作才能断开,客户端同时又为自己定义一个定时器,因为不知道刚才说的这句话能不能准确到达服务端(网络不稳定或者其他因素引起的网络原因),默认时间定为两个通信的最大时间之和,超出这个时间就默认服务器端已经接收到了自己的确认信息,此时客户端就关闭自身连接,服务器端一旦接收到客户端发来的确定通知就立刻关闭服务器端的连接。

到此为止双方整个通信过程就此终结。这里要声明一下:断开链接不一定就是客户端,谁都可以先发起断开指令,另外客户端和服务端是没有固定标准的,谁先发起请求谁就是客户端。
这里写图片描述

“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”

17、 面向对象
https://blog.csdn.net/coolwriter/article/details/79700842
1、 什么是虚函数?什么是纯虚函数?
虚函数声明如下: virtual ReturnType FunctionName(Parameter);引入虚函数是为了动态绑定。
纯虚函数声明如下:virtual ReturnType FunctionName()= 0;引入纯虚函数是为了派生接口。
虚函数
定义一个函数为虚函数,不代表函数为不被实现的函数
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数

C++中的虚函数的作用主要是实现了多态的机制。基类定义虚函数,子类可以重写该函数;在派生类中对基类定义的虚函数进行重写时,需要再派生类中声明该方法为虚方法。

当子类重新定义了父类的虚函数后,当父类的指针指向子类对象的地址时,[即B b; A a = &b;] 父类指针根据赋给它的不同子类指针,动态的调用子类的该函数,而不是父类的函数(如果不使用virtual方法,请看后面),且这样的函数调用发生在运行阶段,而不是发生在编译阶段,称为动态联编。而函数的重载可以认为是多态,只不过是静态的。注意,非虚函数静态联编,效率要比虚函数高,但是不具备动态联编能力。
如果使用了virtual关键字,程序将根据引用或指针指向的 对 象 类 型 来选择方法,否则使用引用类型或指针类型来选择方法。
纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”
virtual void funtion1()=0;
引入原因
  1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
  2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。
纯虚函数最显著的特征是:它们必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。
定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。
2、 纯虚函数是否可以实例化
c++中包含纯虚函数的类是不允许被实例化的!!!,进一步说,如果继承该类的类不重写这个纯虚函数的话,也是不允许被实例化的。即,包含纯虚函是的类派生出来的类都必须重写这个纯虚函数!
3、不能声明为虚函数的有哪些
1、静态成员函数; 2、类外的普通函数; 3、构造函数; 4、友元函数
虚函数是为了实现多态特性的。虚函数的调用只有在程序运行的时候才能知道到底调用的是哪个函数,其是有有如下几点需要注意:
(1) 类的构造函数不能是虚函数
构造函数是为了构造对象的,所以在调用构造函数时候必然知道是哪个对象调用了构造函数,所以构造函数不能为虚函数。
(2) 类的静态成员函数不能是虚函数
类的静态成员函数是该类共用的,与该类的对象无关,静态函数里没有this指针,所以不能为虚函数。
(3)内联函数
内联函数的目的是为了减少函数调用时间。它是把内联函数的函数体在编译器预处理的时候替换到函数调用处,这样代码运行到这里时候就不需要花时间去调用函数。inline是在编译器将函数类容替换到函数调用处,是静态编译的。而虚函数是动态调用的,在编译器并不知道需要调用的是父类还是子类的虚函数,所以不能够inline声明展开,所以编译器会忽略。
(4)友元函数
友元函数与该类无关,没有this指针,所以不能为虚函数。

18、 快速排序的思想、时间复杂度、实现以及优化方法
1、 快速排序的三个步骤:
(1)选择基准:在待排序列中,按照某种方式挑出一个元素,作为 “基准”(pivot);
(2)分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大;
(3)递归地对两个序列进行快速排序,直到序列为空或者只有一个元素。
2、 快速排序代码的实现

void quicksort(vector<int> &v,int left, int right)
{  
    if(left < right)//false则递归结束
    {    
        int key=v[left];//基数赋值
        int low = left;                
        int high = right;   
        while(low < high)    //当low=high时,表示一轮分割结束
        {                        
            while(low < high && v[high] >= key)//v[low]为基数,从后向前与基数比较
            {                                
                high--;                        
            }
            swap(v[low],v[high]);

            while(low < high && v[low] <= key)//v[high]为基数,从前向后与基数比较
            {                                
                low++;                        
            }      
            swap(v[low],v[high]);
        }                 
        //分割后,对每一分段重复上述操作
        quicksort(v,left,low-1);               
        quicksort(v,low+1,right);
    }
}

3、 优化
常见的做法“三数取中”法(序列太短还要结合其他排序法,如插入排序、选择排序等)

void insertion_sort(vector<int> &unsorted,int left, int right)  //插入排序算法        
{          
    for (int i = left+1; i <= right; i++)          
    {          
        if (unsorted[i - 1] > unsorted[i])          
        {         
            int temp = unsorted[i];         
            int j = i;         
            while (j > left && unsorted[j - 1] > temp)  

            {           
                unsorted[j] = unsorted[j - 1];          
                j--;          
            }     
            unsorted[j] = temp;      
        }      
    }     
}  

int SelectPivotOfThree(vector<int> &arr,int low,int high)  //三数取中,同时将中值移到序列第一位  
{    
    int mid = low + (high - low)/2;//计算数组中间的元素的下标    

    //使用三数取中法选择枢轴  
    if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high]    
    {    
        swap(arr[mid],arr[high]);  
    }    
    if (arr[low] > arr[high])//目标: arr[low] <= arr[high]    
    {    
        swap(arr[low],arr[high]);  
    }    
    if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid]    
    {    
        swap(arr[mid],arr[low]);  
    }    
    //此时,arr[mid] <= arr[low] <= arr[high]    
    return arr[low];    
    //low的位置上保存这三个位置中间的值    
    //分割时可以直接使用low位置的元素作为枢轴,而不用改变分割函数了    
}
//三数取中函数不仅仅要实现取中,还要将中值移到最低位,从而保证原分割函数依然可用。

19、 C语言中变量的存储类型有哪些
https://www.cnblogs.com/zhangruilin/p/5769842.html
C语言中的存储类型有auto,extern,register,static四种
auto:自动变量,可以不写,默认情况下局部变量就是auto
register:寄存器变量,把经常被调用的变量声明为寄存器变量,快速存储
extern: 静态存储类别的变量,在另一个文件中,通过extern 全局变量名的声明
static:静态存储类别的变量
静态存储类别的变量,从程序的开始处就存在,其生命期伴随整个程序,一直存在程序的执行过程。static变量仅仅在变量的作用范围内可见,而全局变量是在所有地方都可见的,这就是static变量与全局变量的区别,全局变量是不显示用static修饰的全局变量,但全局变量默认是静态的,作用域是整个工程。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值