说明:
- 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);
- 文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要经过认真思考,答案不重要,重要的是通过题目理解所考知识点,好应对题目更多的变化;
- 博主与大家一起学习,一起刷题,共同进步;
- 写文不易,麻烦给个三连!!!
1.C++中的组合是什么?它与继承相比有什么优缺点吗?
答案:
继承
继承是Is a 的关系,比如说Student继承Person,则说明Student is a Person。继承的优点是子类可以重写父类的方法来方便地实现对父类的扩展。
继承的缺点:
- 父类的内部细节对子类是可见的。
- 子类从父类继承的方法在编译时就确定下来了,所以无法在运行期间改变从父类继承的方法的行为。
- 如果对父类的方法做了修改的话(比如增加了一个参数),则子类的方法必须做出相应的修改。所以说子类与父类是一种高耦合,违背了面向对象思想。
组合
组合也就是设计类的时候把要组合的类的对象加入到该类中作为自己的成员变量。
组合的优点:
- 当前对象只能通过所包含的那个对象去调用其方法,所以所包含的对象的内部细节对当前对象时不可见的。
- 当前对象与包含的对象是一个低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码。
- 当前对象可以在运行时动态的绑定所包含的对象。可以通过set方法给所包含对象赋值。
组合的缺点:
- 容易产生过多的对象。
- 为了能组合多个对象,必须仔细对接口进行定义
2.函数调用过程栈的变化,返回值和参数变量哪个先入栈?
答案:
- 调用者函数把被调函数所需要的参数按照与被调函数的形参顺序相反的顺序压入栈中,即:从右向左依次把被调函数所需要的参数压入栈;
- 调用者函数使用call指令调用被调函数,并把call指令的下一条指令的地址当成返回地址压入栈中(这个压栈操作 隐含在call指令中);
- 在被调函数中,被调函数会先保存调用者函数的栈底地址(push ebp),然后再保存调用者函数的栈顶地址,即:当前被调函数的栈底地址(mov ebp,esp);
- 在被调函数中,从ebp的位置处开始存放被调函数中的局部变量和临时变量,并且这些变量的地址按照定义时的 顺序依次减小,即:这些变量的地址是按照栈的延伸方向排列的,先定义的变量先入栈,后定义的变量后入栈;
3.define、const、typedef、inline的使用方法?他们之间有什么区别?
答案:
const与#define的区别:
1.const定义的常量是变量带类型,而#define定义的只是个常数不带类型;
2.define只在预处理阶段起作用,简单的文本替换,而const在编译、链接过程中起作用;
3.define只是简单的字符串替换没有类型检查。而const是有数据类型的,是要进行判断的,可以避免一些低级错误;
4.define预处理后,占用代码段空间,const占用数据段空间;
5.const不能重定义,而define可以通过#undef取消某个符号的定义,进行重定义;
6.define独特功能,比如可以用来防止文件重复引用。
#define和别名typedef的区别
1.执行时间不同,typedef在编译阶段有效,typedef有类型检查的功能;#define是宏定义,发生在预处理阶段,不进行类型检查;
2.功能差异,typedef用来定义类型的别名,定义与平台无关的数据类型,与struct的结合使用等。#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
3.作用域不同,#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef有自己的作用域。
define与inline的区别
1.#define是关键字,inline是函数;
2.宏定义在预处理阶段进行文本替换,inline函数在编译阶段进行替换;
3.inline函数有类型检查,相比宏定义比较安全;
4.你知道printf函数的实现原理是什么吗?''
答案:
在C/C++中,对函数参数的扫描是从后向前的。
C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来,在计算机的内存中,数据有2块,一块是堆,一块是栈(函数参数及局部变量在这里),而栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在最上面,就是说在所有参数的最后面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。
printf的第一个被找到的参数就是那个字符指针,就是被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数来判断参数个数及数据类型,通过这些就可算出数据需要的堆栈指针的偏移量了,下面给出printf("%d,%d",a,b);(其中a、b都是int型的)的汇编代码。
5.cout和printf有什么区别?
答案:
很多人认为cout<<是一个函数,其实不是的,它是类std::ostream的全局对象。
cout<<后可以跟不同的类型是因为cout<<已存在针对各种类型数据的重载,所以会自动识别数据的类型。
输出过程会首先将输出字符放入缓冲区,然后输出到屏幕。
cout是有缓冲输出:
cpp
cout < < "abc " < <endl;
或cout < < "abc\n "; cout < <flush; 这两个才是一样的.
flush立即强迫缓冲输出。
printf是行缓冲输出,不是无缓冲输出。
6.当程序中有函数重载时,函数的匹配原则和顺序是什么?
答案:
1.名字查找
2.确定候选函数
3.寻找最佳匹配
7.如何在不使用额外空间的情况下,交换两个数?你有几种方法
答案:
cpp
1) 算术
x = x + y;
y = x - y;
x = x - y;
2) 异或
x = x^y;// 只能对int,char..
y = x^y;
x = x^y;
8.volatile关键字的作用?
答案:
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。声明时语法:int volatile vInt; 当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
volatile用在如下的几个地方:
- 中断服务程序中修改的供其它程序检测的变量需要加volatile;
- 多任务环境下各任务间共享的标志应该加volatile;
- 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
9.你知道const char* 与string之间的关系是什么吗?
答案:
- string 是c++标准库里面其中一个,封装了对字符串的操作,实际操作过程我们可以用const char*给string类初始化
- 转化关系如下所示:
cpp
a) string转const char*
string s = "abc";
const char* c_s = s.c_str();
b) const char* 转string,直接赋值即可
const char* c_s = "abc";
string s(c_s);
c) string 转char*
string s = "abc";
char* c;
const int len = s.length();
c = new char[len+1];
strcpy(c,s.c_str());
d) char* 转string
char* c = "abc";
string s(c);
e) const char* 转char*
const char* cpc = "abc";
char* pc = new char[strlen(cpc)+1];
strcpy(pc,cpc);
f) char* 转const char*,直接赋值即可
char* pc = "abc";
const char* cpc = pc;
10.成员初始化列表会在什么时候用到?它的调用过程是什么?
答案:
- 当初始化一个引用成员变量时;
- 初始化一个const成员变量时;
- 当调用一个基类的构造函数,而构造函数拥有一组参数时;
- 当调用一个成员类的构造函数,而他拥有一组参数;
- 编译器会一一操作初始化列表,以适当顺序在构造函数之内安插初始化操作,并且在任何显示用户代码前。list中的项目顺序是由类中的成员声明顺序决定的,不是初始化列表中的排列顺序决定的。
11.说一说strcpy、sprintf与memcpy这三个函数的不同之处
答案:
操作对象不同:
- strcpy的两个操作对象均为字符串
- sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串
- memcpy的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。
执行效率不同:
memcpy最高,strcpy次之,sprintf的效率最低。
实现功能不同:
- strcpy主要实现字符串变量间的拷贝
- sprintf主要实现其他数据类型格式到字符串的转化
- memcpy主要是内存块间的拷贝