C/C++服务器开发常见面试题(二)

12 篇文章 2 订阅
这篇博客详细探讨了C++服务器开发的面试重点,包括STL容器的实现原理,如Vector、Map和Set,高危库函数如strcpy的风险,多态、虚函数和动态绑定的概念,以及C++中的多重继承问题。还讨论了智能指针、枚举与#define宏的区别、函数重载和派生类的创建过程等。面试者需要理解MVC模式、类型转换以及如何使用和避免内存泄漏。
摘要由CSDN通过智能技术生成

22.STL各容器的实现原理(必考)

1)Vector顺序容器,是一个动态数组,支持随机插入、删除、查找等操作,在内存中是一块连续的空间。在原有空间不够情况下自动分配空间,增加为原来的两倍。vector随机存取效率高,但是在vector插入元素,需要移动的数目多,效率低下。

注:vector动态增加大小时是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector空间重新配置,指向原vector的所有迭代器就都失效了。

2)Map关联容器,以键值对的形式进行存储,方便进行查找。关键词起到索引的作用,值则表示与索引相关联的数据。红黑树的结构实现,插入删除等操作都在O(logn)时间内完成。

3) Set是关联容器,set每个元素只包含一个关键字。set支持高效的关键字检查是否在set中。set也是以红黑树的结构实现,支持高效插入、删除等操作。

23.哪些库函数属于高危函数,为什么?

strcpy 赋值到目标区间可能会造成缓冲区溢出,因为如果目的地址所指的内存空间不够大,可能会造成缓冲溢出的错误情况

24.STL有7种主要容器:

vector,list,deque,map,multimap,set,multiset

25.你如何理解MVC。简单举例来说明其应用。

MVC模式是observer 模式的一个特例,现在很多都是java的一些框架,MFC的,PHP的。

26.C++特点是什么,多态实现机制?(面试问过)多态作用?两个必要条件?

C++中多态机制主要体现在两个方面,一个是函数的重载,一个是接口的重写。接口多态指的是“一个接口多种形态”。每一个对象内部都有一个虚表指针,该虚表指针被初始化为本类的虚表。所以在程序中,不管你的对象类型如何转换,但该对象内部的虚表指针是固定的,所以呢,才能实现动态的对象函数调用,这就是C++多态性实现的原理。

多态的基础是继承,需要虚函数的支持,简单的多态是很简单的。子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数等等

作用:
1.隐藏实现细节,代码能够模块化;
2. 接口重用:为了类在继承和派生的时候正确调用。

必要条件:

  1. 一个基类的指针或者引用指向派生类的对象
  2. 虚函数

27.多重继承有什么问题? 怎样消除多重继承中的二义性?

1)增加程序的复杂度,使程序的编写和维护比较困难,容易出错;

2)继承类和基类的同名函数产生了二义性,同名函数不知道调用基类还是继承类,C++中使用虚函数解决这个问题

3)继承过程中可能会继承一些不必要的数据,对于多级继承,可能会产生数据很长

可以使用成员限定符虚函数解决多重继承中函数的二义性问题。

28.求两个数的乘积和商数,该作用由宏定义来实现

#define product(a,b) ((a)*(b))
#define divide(a,b)  ((a)/(b))

29.什么叫静态关联,什么叫动态关联

多态中,静态关联是程序在编译阶段就能确定实际执行动作,程序运行才能确定叫动态关联。

确认具体对象的过程叫关联(binding),在这里指把一个函数与类对象捆绑在一起,建立关联。
函数重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一个类,其过程称为静态关联,由于是在运行前关联的,所以又叫早期关联。
在运行时,基类指针变量指向了某个类对象,然后通过这个基类指针去调用虚函数。由于是在运行时把虚函数与对象“绑定”在一起, 因此,此过程称为动态关联。由于动态关联是在编译后运行阶段进行的,所以又称为滞后关联。

30.什么叫智能指针?常用的智能指针有哪些?智能指针的实现?

智能指针是一个存储指向动态分配(堆)对象指针的类,构造函数传入普通指针,析构函数释放指针。栈上分配,函数或程序结束自动释放,防止内存泄露。使用引用计数器,类与指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建,增加引用计数;对一个对象进行赋值时,减少引用计数,并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数,当引用计数减至0,则删除基础对象。

std::auto_ptr,不支持复制(拷贝构造函数)和赋值(operator =),编译不会提示出错。

C++11引入的unique_ptr, 也不支持复制和赋值,但比auto_ptr好,直接赋值会编译出错。

C++11或boost的shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。还有Weak_ptr

31.枚举与#define 宏的区别

1)#define 宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
2)可以调试枚举常量,但是不能调试宏常量。
3)枚举可以一次定义大量相关的常量,而#define 宏一次只能定义一个。

32.介绍一下函数的重载

函数重载是一种特殊情况,C++允许在同一作用域中声明几个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。

33.派生新类的过程要经历三个步骤

1.吸收基类(父类)成员
2.改造基类(父类)成员
3.添加新成员

34.面向对象的三个基本特征,并简单叙述之?

1)封装:是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
2)继承:继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为
3)多态:允许一个基类的指针或引用指向一个派生类对象,一个类实例(对象)的相同方法在不同情形有不同表现形式

35.多态性体现都有哪些?动态绑定怎么实现?

多态性是一个接口,多种实现,是面向对象的核心。 编译时多态性:通过重载函数实现。运行时多态性:通过虚函数实现,结合动态绑定。真正和多态相关的是**“覆盖”**。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。

36.虚函数,虚函数表里面内存如何分配?

编译时若基类中有虚函数,编译器为该的类创建一个一维数组的虚表,存放是每个虚函数的地址。基类和派生类都包含虚函数时,这两个类都建立一个虚表。**构造函数中进行虚表的创建和虚表指针的初始化。**在构造子类对象时,要先调用父类的构造函数,初始化父类对象的虚表指针,该虚表指针指向父类的虚表。执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。每一个类都有虚表。虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。当用一个指针/引用调用一个函数的时候,被调用的函数是取决于这个指针/引用的类型。即如果这个指针/引用是基类对象的指针/引用就调用基类的方法;如果指针/引用是派生类对象的指针/引用就调用派生类的方法,当然如果派生类中没有此方法,就会向上到基类里面去寻找相应的方法。这些调用在编译阶段就确定了。当涉及到多态性的时候,采用了虚函数和动态绑定,此时的调用就不会在编译时候确定而是在运行时确定。不在单独考虑指针/引用的类型而是看指针/引用的对象的类型来判断函数的调用,根据对象中虚指针指向的虚表中的函数的地址来确定调用哪个函数。

37. 纯虚函数如何定义?含有纯虚函数的类称为什么?为什么析构函数要定义成虚函数?

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。纯虚函数是虚函数再加上= 0。

virtual void fun ()=0

含有纯虚函数的类称为抽象类。在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。
如果析构函数不是虚函数,那么释放内存时候,编译器会使用静态联编,认为p就是一个基类指针,调用基类析构函数,这样子类对象的内存没有释放,造成内存泄漏。定义成虚函数以后,就会动态联编,先调用子类析构函数,再基类。

38. C++中哪些不能是虚函数?

1)普通函数只能重载,不能被重写,因此编译器会在编译时绑定函数。
2)构造函数是知道全部信息才能创建对象,然而虚函数允许只知道部分信息。
3)内联函数在编译时被展开,虚函数在运行时才能动态绑定函数。
4)友元函数 因为不可以被继承。
5)静态成员函数 只有一个实体,不能被继承。父类和子类共有。

39. 类型转换有哪些?各适用什么环境?dynamic_cast转换失败时,会出现什么情况?

静态类型转换,static_cast,基本类型之间和具有继承关系的类型。
例子A,double类型转换成int。B,将子类对象转换成基类对象。

常量类型转换,const_cast, 去除指针变量的常量属性。
无法将非指针的常量转换为普通变量。

动态类型转换,dynamic_cast,运行时进行转换分析的,并非在编译时进行。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast用于类层次间的向上转换和向下转换,还可以用于类间的交叉转换。在类层次间进行向上转换,即子类转换为父类,此时完成的功能和static_cast是相同的,因为编译器默认向上转换总是安全的。向下转换时,dynamic_cast具有类型检查的功能,更加安全。类间的交叉转换指的是子类的多个父类之间指针或引用的转换。该函数只能在继承类对象的指针之间或引用之间进行类型转换,或者有虚函数的类。

40. 如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

#ifdef __cplusplus
cout<<"C++";
#else
cout<<"c";
#endif

41. 为什么要用static_cast转换而不用c语言中的转换?

Static_cast转换,它会检查类型看是否能转换,有类型安全检查。
比如,这个在C++中合法,但是确实错误的。

A* a= new A;
B* b = (B*)a;

42. 操作符重载(+操作符),具体如何去定义?

除了类属关系运算符”.”、成员指针运算符”.*”、作用域运算符”::”sizeof运算符和三目运算符”?:”以外,C++中的所有运算符都可以重载。

<返回类型说明符> operator <运算符符号>(<参数表>){}

重载为类的成员函数和重载为类的非成员函数。参数个数会不同,应为this指针。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值