c++基础面试题总结

Posted 坚持奋斗的李洛克

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++基础面试题总结相关的知识,希望对你有一定的参考价值。

如果内容有误,请留言提出,谢谢

1 面向过程与面向对象的本质区别思考

面向过程就像是一个细心的管家,事无具细的都要考虑到。而面向对象就像是个家用电器,你只需要知道他的功能,不需要知道它的工作原理。(封装性)
面向过程”是一种是事件为中心的编程思想。就是分析出解决问题所需的步骤,然后用函数把这写步骤实现,并按顺序调用。面向对象是以“对象”为中心的编程思想。而对象就是类的实例化。
简单的举个例子:汽车发动、汽车到站
这对于“面向过程”来说,是两个事件,汽车启动是一个事件,汽车到站是另一个事件,面向过程编程的过程中我们关心的是事件,而不是汽车本身。针对上述两个事件,形成两个函数,之后依次调用。然而这对于面向对象来说,我们关心的是汽车这类对象,两个事件只是这类对象所具有的行为。而且对于这两个行为的顺序没有强制要求。比如:Bus类,包括发动、到站之两个行为
面向对象是将事物高度抽象化。
面向过程是一种自顶向下的编程
面向对象必须先建立抽象模型,之后直接使用模型就行了。

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

2 拷贝构造函数

有默认拷贝构造函数

类名(类名& 对象)

1).初始化另外一个对象时
2).调用函数,实参赋给形参,系统自动调用拷贝构造函数
3).返回值是类对象时。

3 用内联取代宏原因:

1.内联函数在运行时可调试,而宏定义不可以;
2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
3.内联函数可以访问类的成员变量,宏定义则不能;
4.在类中声明同时定义的成员函数,自动转化为内联函数。

在C中,常用预处理语句#define来代替一个函数定义。

*编程题:字符串中找到子串。

4 c语言中的struct 和c++中的struct

c++中的struct,成员变量默认为public,struct可有构造函数和成员函数

5 野指针 有任意指向。 出现情况

1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向NULL。
(2)指针 p 被 free 或者 delete 之后,没有置为 NULL。解决办法:指针指向的内存空间被释放 后指针应该指向NULL。
(3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间
并且让指针指向NULL。

6 预编译命令

宏定义指令 #define PI 3.1415926
文件包含指令
#include "文件名" 先找当前文件目录,后找库目录 #include <文件名> 先检查预定义的目录,类库路径

7 条件编译指令

格式:(1)


    #ifdef 标识符
    程序段1
    #else
    程序段2
    #endif#ifdef
    程序段1
    #endif
    当标识符已经定义时,程序段1才参加编译。
    格式:(2#ifndef 标识符
    #define 标识1
    程序段1
    #endif
    如果标识符没有被定义,则重定义标识1,且执行程序段1

使用条件编译可以使目标程序变小,运行时间变短。
预编译使问题或算法的解决方案增多,有助于我们选择合适的解决方案。

8 sizeof是何方神圣sizeof乃C/C++中的一个操作符(operator)是也,简单的说其作用就是返回一个对象或者类型所占的内存字节数。

32位系统,vc编译器中,
short占 2 字节,
int 、float、long 都占 4 字节,
只有double 占8 字节

64位系统
short占 2 字节,
int 、float、都占 4 字节,
long, double,指针变量,long long 占8 字节

9 assert是宏,而不是函数

类型检查和单元测试 异常处理
assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行

10 链表和数组

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
从逻辑结构来看
1. 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
2. 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到下一个元素
从内存存储来看
1. (静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小
2. 链表从堆中分配空间, 自由度大但是申请管理比较麻烦

11 队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作
线性表有:栈,队列,字符串,数组

12 extern “C”

extern “C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码

13 内存泄漏

内存泄漏:内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
堆泄漏:malloc,realloc new等从堆中分配的一块内存用完后没有释放。
Resource Leak :对于系统资源使用之前要仔细看起使用方法,防止错误使用或者忘记释放掉系统资源。

详见:http://blog.csdn.net/na_he/article/details/7429171

内存溢出:内存分配未成功,却使用了它
常用解决办法是,在使用内存之前检查指针是否为NULL
内存分配虽然成功,但是尚未初始化就引用它。
内存分配成功并且已经初始化,但操作越过了内存的边界
不要忘记为数组和动态内存赋初值

15 new 和 malloc

new 返回指定类型的指针,并且可以自动计算所需要大小。
malloc 函数返回的是 void * 类型,由我们计算字节数。
1、malloc与free是C++/C语言的内存分配标准库函数,属于stdlib库;new/delete是C++的操作运算符。它们都可用于申请动态内存和释放内存。
2、 对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求 。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
3、C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数, new 不止是分配内存,而且会调用类的构造函数 ,同理delete会调用类 的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数。
4、C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存;
6、 内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。
7、new可以认为是malloc加构造函数的执行。
new出来的指针是直接带类型信息的, 而malloc返回的都是void指针 。
8、new是保留字,不需要头文件支持;malloc需要头文件库函数支持。

16 static的作用

static在C和C++中的用法和区别
static主要有三个作用:

(1)局部静态变量
static局部变量在所处模块在初次运行时进行初始化工作,且只操作一次。 对于局部静态变量,如果不赋初值,编译期会自动赋初值0或空字符,
(2)外部静态变量/函数
对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区,生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.
(3)静态数据成员/成员函数
表示属于一个类而不是属于此类的任何特定对象的变量和函数.

17 栈溢出

Stack 是一块固定大小的连续内存,受运行时管理,无需用户自行分配和回收;当函数调用嵌套层次非常深时会产生 Stack overflow(堆栈溢出)错误,如递归调用、循环调用、消息循环、大对象参数、大对象局部变量等都容易触发堆栈溢出;

18大数求余

///结合一些模运算的性质来考虑,比如,对多个数字的相加再求模和先对中间部分结果求模再相加后面的数再求模的结果是一样的
//即 (X+Y+Z)modP =((X+Y)modP+ Z)modP = ((XmodP +Y)modP+Z)modP
//由此可以推出下面的代码:
for (int i = 0; i < strlen(s); ++i)

    sum = sum*10 + s[i]-48;//这里要把每个字符的ASCII码减去48成为数字
    printf("每个数:%ld\\n", sum);
    sum %= modNumber;
    printf("余数:%ld\\n", sum);

19 简述strcpy、sprintf与memcpy的区别

(1)操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,
目的操作对象是字符串,memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。
(2)执行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
(3)实现功能不同,strcpy主要实现字符串变量间的拷贝,sprintf主要实现其他数据类型格式到字
符串的转化,memcpy主要是内存块间的拷贝。

20 指针和引用主要有以下区别

(1)引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。
(2)引用初始化以后不能被改变,指针可以改变所指的对象。
(3)不存在指向空值的引用,但是存在指向空值的指针。

21 typedef 和define有什么区别

(1)用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义常量,以及书写复杂使用频繁的宏。
(2)执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
(3)作用域不同:typedef 有作用域限定。define不受作用域约束,只要是在define声明后的引用都是正确的。
(4)对指针的操作不同:typedef和define定义的指针时有很大的区别。
注意:typedef定义是语句,因为句尾要加上分号。而define不是语句,千万不能在句尾加分号

22 指针常量与常量指针区别

常量指针,表示这个指针乃是一个指向常量的指针(变量)。
指针常量,指针常量的本质是一个常量。

23 在构造函数中需要初始化列表初始化的有如下三种情况

1.带有const修饰的类成员 ,如const int a ;
2.引用成员数据,如 int& p;
3.带有引用的类变量
补充:初始化成员列表是按照成员变量的声明顺序初始化的。

24 c++中接口与实现

接口的作用,就是提供一个与其他系统交互的方法。
根据c++的特点,我们可以采用纯虚函数的方式来实现。这样做的好处是能够实现封装和多态。
实现接口是通过继承接口的子类来实现的,不同的子类可以实现不同效果,即使所谓多态。

25 预处理、编译,链接阶段的任务

A: 预处理是 C 语言程序从源代码变成可执行程序的第一步,主要是 C 语言编译器对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。
B: 编译之前,C 语言编译器会进行词法分析、语法分析 ,接着会把源代码翻译成中间语言,即汇编语言 。 编译程序工作时,先分析,后综合,从而得到目标程序。所谓分析,是指词法分析和语法分析;所谓综合是指代码优化,存储分配和代码生成。 值得一提的是,大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,但也有的编译程序则先产生汇编语言一级的符号代码文件,然后再调用汇编程序进行翻译加工处理,最后产生可执行的机器语言目标文件。
C: 链接是处理可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息( 一般是虚拟内存地址 ) 的过程。如果链接过程中所有符号都有定义,链接成功,生成可执行文件;否则链接失败。

26 析构函数,构造函数 基本要求

1.构造函数和析构函数没有返回值,如果有返回值就要显示的调用函数。
2.构造函数可以重载,析构函数不能被重载
3.构造函数和析构函数不能被继承

27高级语言和低级语言的区别

高级语言:实现效率高,执行效率低,对硬件的可控性弱,目标代码大,可维护性好,可移植性好

低级语言:实现效率低,执行效率高,对硬件的可控性强,目标代码小,可维护性差,可移植性差

28

一个常量或标识符是最简单的表达式
求余 余数符号与被除数符号相同

29 三种继承中访问权限

C++中类访问权限控制:
第一:private, public, protected 访问标号的访问范围,在没有继承的情况下:
private:
只能由1.该类中的函数、2.其友元函数访问。 不能被任何其他访问,该类的对象也不能访问。

protected:
可以被1.该类中的函数、2.子类的函数、以及3.其友元函数访问。 但不能被该类的对象访问。

public:
可以被1.该类中的函数、2.子类的函数、3.其友元函数访问,也可以由4.该类的对象访问。
注:友元函数包括3种:设为友元的普通的非成员函数;设为友元的其他类的成员函数;设为友元类中的所有成员函数。
1. 三种继承方式不影响子类对父类的访问权限,子类对父类只看父类的访问控制权。
2. 继承方式是为了控制子类(也称派生类)的调用方(也叫用户)对父类(也称基类)的访问权限。
3. public、protected、private三种继承方式,相当于把父类的public访问权限在子类中变成了对应的权限。

30 虚继承和虚函数继承

通俗的讲,虚继承就是为了节约内存的,他是多重继承中的特有的概念。适用与菱形继承形式。
如:类B、C都继承类A,D继承类B和C。为了节省内存空间,可以将B、C对A的继承定义为虚拟继承,此时A就成了虚拟基类。
class A;
class B:vitual public A;
class C:vitual public A;
class D:public B,public C;

虚函数继承就是覆盖。即基类中的虚函数被派生类中的同名函数所覆盖。

31 static全局变量与普通的全局变量有什么区别?

  全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。
  从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
   static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
   static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
   static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

32 运算符优先级

基本的优先级需要记住:
指针最优,单目运算优于双目运算。如正负号。
先乘除(模),后加减。
先算术运算,后移位运算,最后位运算。请特别注意:1 << 3 + 2 & 7等价于 (1 << (3 + 2))&7.
逻辑运算最后计算

33 大端模式,小端模式

Big-Endian和Little-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

34 二进制码,补码

二进制码就是源码,有符号数首位用0,1表示,+7的8位二进制原码是00000111,-7的8位二进制原码是10000111。但是计算机运算源码不方便,所以将源码改成补码,正数的补码就是源码,负数的补码是源码取反加1,计算机中存储的就是补码。-7的补码:11111001。
short int a = 0x8000;//这里是补码,是负数,转换成源码就是-2^15=-32768

35 编程中遇到的问题

1)对话框绑定了变量和控件,调用DoModal 后,相应的变量没有数据
答:请在对话框的“确定”按钮的命令响应函数中添加UpdateData函数
2)我在程序中写了一个字符串,为神马提示“不能将参数从“const char [?]”转换为“ACHAR *””?
答:这与VS 2005或者VS 2008的Unicode编码有关,可以有两种解决方案。 a. 菜单 项目属性,打开项目属性对话框。在配置属性常规中,找到字符集:
“使用Unicode字符集”,修改为“使用多字节字符集”。这个设置适合于在项目刚刚创建时进行设置,不然可能会带来其他的错误。
b. 将该字符串改为L””或者_T(“”),例如字符串”abc”,修改为L”abc”或者_T(“abc”)。
3)内存不可写
由于指针的内存未分配导致的或者是常量字符串也不可写,销毁的指针不可再用

36不可重载的运算符

不能重载的5个运算符

.    
?: 
siezof 
::     
.*     

37 MFC中,添加的响应函数后,编译出错

1>project3Dlg.obj : error LNK2001: 无法解析的外部符号 “public: void __thiscall CAboutDlg::OnAdduser(void)” (?OnAdduser@CAboutDlg@@QAEXXZ)
原因添加的响应函数放在CAboutDlg类里面了
解决方法:打开类向导,找到相应的ID的响应函数,然后删除。

以上是关于c++基础面试题总结的主要内容,如果未能解决你的问题,请参考以下文章

c++面试题总结1

C++面试题

Android 面试题总结之Android 基础

C++面试题(算法基础-排序算法)

重拾图形图像处理 ---- 笔试面试题:三维重建相关

JS万字整理JavaScript相关基础技术面试题总结 - 前端面试必备 - 基础知识总结 - 秋招冲鸭