c/c++相关面试准备笔记1

在c++程序中调用被C编译器编译后的函数,为何要加extern  “C”?  c++

  C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不一样。C++提供了C链接交换指定符号ectern  “C”解决名字匹配问题。程序员

头文件中ifndef/define/endif是干什么用的?算法

  防止该头文件被重复引用。编程

评价一下C与C++的各自特色。windows

  C是一种结构化语言,重点在于算法和数据结构。C程序的设计首先考虑的是如何经过一个过程,对输入进行运算处理获得输出。而对于C++,首先考虑的是如何构造一个对象模型,让这个模型可以契合与之对应的问题域,这样就能够经过获取对象的状态信息获得输出或实现过程控制。数组

const用途有哪些?安全

  在C程序中,const的用法主要有定义常量、修饰函数参数、修饰函数返回值。在C++程序中,它还能够修饰函数的定义体,定义类中的某个成员函数为恒态函数,即不改变类中的数据成员。被const修饰的东西都受到强制保护,能够预防意外的变更,可以提升程序的健壮性。数据结构

const与#define相比有什么不一样?多线程

  C++语言能够用const定义常量,也能够用#define定义常量,可是前者比后者有更多的优势:模块化

  const常量有数据类型,而宏常量没有数据类型。编译器对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,而且在字符替换中可能产生意料不到的错误;

  有些集成化的调试工具能够对const常量进行调试,可是不能对宏常量进行调试。在C++程序中只使用const常量而不使用宏常量,即const常量彻底取代宏常量。

mutable关键字:

  在C++中,mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即便在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也能够被修改:

class  ST  { 
public:
    int a; 
    mutable int showCount; 
    void Show()const; 
}; 

void ST::Show()const{ 
    //a=1;//错误,不能在const成员函数中修改普通变量 
    showCount++;//正确 
}

  mutable只能修饰非静态数据成员;

volatile修饰符:

  volatile修饰的数据编译器不可对其进行执行期寄存于寄存器的优化。这种特性是为了知足多线程同步、中断、硬件编程等特殊需求。遇到这个关键字声明的变量,编译器对访问该变量的代码就再也不进行优化,从而能够提供对特殊地址的直接访问。主要用在以下几个地方:

  中断服务程序中修改的供其余程序检测的变量须要加volatile;

  多任务环境下各任务间共享的标志加volatile;

  存储器映射的硬件寄存器一般也要加volatile说明,由于每次对他的读写均可能有不一样的意义;

数据对齐:指的是数据所在的内存地址必须是该数据长度的整数倍。字节对齐的细节和编译器实现相关,但通常而言,知足三个准则:

  结构体变量的首地址可以被其最宽基本类型成员的大小所整除;

  结构体每一个成员相对于结构体首地址的偏移量都是成员大小的整数倍,若有须要编译器会在成员之间加上填充字节;

  结构体的总大小为结构体最宽基本类型成员大小的整数倍,若有须要编译器会在最末一个成员以后加上填充字节。若是结构体内包含其余结构,会将 其中的成员打散来看其最宽基本类型成员大小;

  可使用编译器的pack指令调整结构体对齐方式。

sizeof与strlen的区别:

  sizeof操做符的结果类型是size_t,他在头文件中的typedef为unisigned int类型。该类型保证能容纳实现所创建的最大对象的字节大小;

  sizeof是运算符,strlen是函数;

  sizeof能够用类型作参数,strlen只能用char*做为参数,且必须是以’\0’结尾的。

  数组作sizeof的参数不退化,传递给strlen就退化为指针。

  大部分煸诩程序在编译时就把sizeof计算过了,是类型或是变量的长度;

  strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占内存的大小;

  sizeof后若是是类型必须加括号,若是是变量名能够不加括号。这是由于sizeof是个操做符而不是函数;

  当使用了一个结构类型或变量时,sizeof返回实际的大小。当使用一静态的空间数组时,sizeof返回所有数组的尺寸。sizeof操做符不能返回被动态分配的数组或外部的数组的尺寸。

  数组做为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,在C++中传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小。若是想在函数内知道数组的大小,须要进入函数时加上一个数组长度的参数;

  计算结构变量的大小就必须讨论数据对齐问题。

  sizeof操做符不能用于函数类型、不彻底类型或位字段。不彻底类型指具备未知存储大小数据的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型。

sizeof的使用场合:

  sizeof操做符的一个主要用途就是与存储分配和I/O系统那样的例程进行通讯。

  用它来看某种类型的对象在内存中所占的单元字节。

  在动态分配一对象时,可让系统知道要分配多少内存;

  便于一些类型的扩充。在windows中有不少结构类型就有一个专门的字段用来存放该类型的字节大小;

  若是操做数是函数中的数组形参或函数类型的形参,建议在涉及操做数字节大小时用sizeof代替常量计算。

  若是操做数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小;

  空类的所占内存为1,单一继承和多重继承的空类的空间仍是1。可是涉及到虚继承的空类所占空间大小为4字节,由于有虚表。

内联函数和宏的差异?

  内联函数和普通函数相比能够加快程序运行的速度,由于不须要中断调用,在编译的时候内联函数能够直接被镶嵌到目标代码中。而宏只是一个简单的替换。

  内联函数要作参数类型检查,这是内联函数跟宏相比的优点;

  inline是指嵌入代码,就是在跳用函数的地方不是跳转,而是把代码直接写到哪里去。对于短小的代码来讲,inline能够带来必定的效率提高,并且和C时代的宏函数相比,inline更安全可靠。但是这个是以增长空间消耗为代价的。至因而否使用inline函数,就须要根据实际状况来取舍了;

内联一边只用于下面的状况:

  一个函数不断被重复调用;

  函数只有简单的几行,且函数不包含for、whlie、switch语句;

  宏在C语言里极其重要,而在C++里面就用的少多了。关于宏的第一规则是毫不应该去使用它,除非你不得不这样作。几乎每一个宏都代表了在程序设计语言里、程序里或者程序员的一个缺陷,由于它将在编译器看到程序的正文以前从新摆布这些正文。宏也是许多程序设计工具的主要麻烦。因此,若是你使用了宏,就应该准备只能从各类工具中获得较少的服务;

  用define定义常量能够用const代替,可是主要const修饰的只读变量不能用来定义数组的维度,也不能放在case关键字的后面;

  宏是在代码处不加任何验证的简单替代,而内敛函数是将代码直接插入到调用处,而减小了普通函数调用时的资源消耗。

  宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。

  inline函数是函数,可是在编译中不单独产生代码,而是将有关代码嵌入到调用处;

指针和引用的区别:

  非空区别:在任何状况下都不能使用指向空值的引用。一个引用必须老是指向某些对象。

  合法性区别:在使用引用以前不须要测试它的合法性。相反,指针则应该老是被测试,防止其为空;

  可修改区别:指针能够被从新赋值以指向另外一个不一样的对象。可是引用则老是指向在初始化是被指向的对象,之后不能改变,可是指定的对象其内容能够改变。

  应用区别:在如下状况下应该使用指针:一是考虑到存在不指向任何对象的可能,二是须要可以在不一样的时刻指向不一样的对象。若是老是只想一个对象而且一旦指向一个对象后就不会改变其指向,那么应该使用引用。

写出函数指针、函数返回指针:

  void (*f)()、void * f()

C++中有了malloc/free,为何还须要new/delete?

  malloc与free是c++/c语言的标准函数,而new/malloc是c++的运算符。它们都用于申请动态内存和释放内存;

  对于非内部数据类型的对象而言,只用malloc/free没法知足动态对象的要求。对象在建立的同时要自动执行构造函数,对象在消亡以前要自动执行析构函数。因为malloc/free是库函数而不是运算符,不在编译器控制权限以内,不可以把执行构造函数和析构函数的任务强加于malloc/free。

  所以c++须要一个能完成动态内存分配和初始化工做的运算符new,以及一个能完成清理与释放工做的运算符delete。new/malloc不是库函数,是运算符;

句柄与指针:

  句柄和指针实际上是两个彻底不一样的概念。windows系统用句柄表及系统资源,隐藏系统的信息。你只要知道有这个东西,而后去调用就好了,他是个32bit的uint。指针则标记某个物理内存地址,二者是不一样的概念。

  句柄是一种指向指针的指针。

介绍一下STL和包容器,如何实现?

  c++的一个新特性就是采用了标准模板库。标准模板库是一个基于模版的容器类库,包括链表、列表、队列和堆栈。标准模板库还包含许多经常使用的算法,包括排序和查找。

  标准模板库的目的是提供对经常使用需求从新开发的一种替代方法。容器是包容其余对象的对象。能够帮助程序员处理一些经常使用的编程任务。标准模板库容器类有两种类型,分别为顺序和关联。顺序容器能够提供对成员的顺序访问和随机访问。

什么是泛型编程?

  stl表明用一致的方式编程是可能的。实际上,他彻底不一样于咱们看到的c++编程,也彻底不一样于大多数教科书里所被描述的方式。stl并非试图用c++编程,只是试图找一种正确的方式来处理软件,寻找一种能够表达个人想法的语言而已。

  泛型编程是一种基于发现高效算法的最抽象表示的编程方法。也就是说,一算法为起点并寻找能使其工做且有效率工做的最通常的必要条件集。泛型编程假定有某些基本的法则在支配软件组件的行为,而且基于这些法则有可能设计可互操做的模块,甚至还可使用此法则去知道咱们的软件设计。stl就是一个泛型编程的例子。c++一种但是使用泛型编程例子的语言。泛型编程中,算法不与任何特定的数据结构或对象类型系在一块儿;

  初始化列表的初始化顺序是根据成员变量的声明顺序来执行的。

 

面对对象编程带来的便利有:良好的可复用性、易维护和良好的可扩充性;

面对对象的基本概念是什么面对对象必须提供对象、类和继承;

对于一个空类,编译器默认产生四个成员函数:默认构造函数、析构函数、拷贝构造函数和赋值函数;

结构与类的区别:

  class中变量默认是private,struct中的变量默认是public。struct能够有构造函数、析构函数,之间也能够继承,等。c++中的struct与class其实意义同样,惟一的不一样就是struct里面默认的访问控制是public,class中默认的访问控制是private。c++中存在struct关键字的惟一意义就是为了让c程序员有个归属感,是为了让c++编译器兼容之前的用c开发的项目;

多态:

  一种接口,多种方法;容许将子类类型的指针赋值给父类类型的指针。多态性是经过虚函数实现的。虚函数就是容许被其子类从新定义的成员函数。而子类从新定义父类虚函数的作法称为覆盖或重写。

覆盖与重载:

  覆盖是指子类从新定义父类的虚函数的作法。而重载,是指容许存在多个同名函数,而这些函数的参数表不一样。重载的函数编译器会给不一样参数的函数修饰一个不一样的名字是静态的。

  封装能够隐藏实现细节,使得代码模块化;继承能够扩展已存在的代码模块;他们的目的都是为了代码重用;而接口是为了实现接口重用。

友元:

  友元是一种定义在类外部的普通函数,但他须要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,可是他能够访问类中的私有成员。友元的做用在于提升程序的运行效率,可是它破坏了类的封装性和隐藏性,使得非成员函数能够访问类的私有成员。

  类对象操做的时候在内部构造是会有一个隐形的this指针。因为Car类是vehicle的派生类,那么当car对象建立的时候,这个this指针就会覆盖到vehicle类的范围,因此派生类可以对基类成员进行操做。

公有继承:

  基类成员对其对象的可见性与通常类及其对象的可见性相同,共有成员可见,其余成员不可见。这里保护成员与私有成员相同;在公有继承中,派生类的对象能够访问基类中的公有成员,派生类的成员函数能够访问基类的公有成员和保护成员;

私有继承:

  积累对其对象的可见性与通常类及其对象的可见性相同,公有成员可见,其余成员不可见;基类的公有成员可和保护成员是可见的,基类的公有成员和保护成员都做为派生类的私有成员,而且不能被这个派生类的子类所访问;基类的私有成员都是不可见的,派生类不可访问基类中的私有成员。因此在私有继承时,积累的成员只能由派生类访问,而没法再往下继承。

保护继承:

  这种继承类型与私有继承的状况相同。二者的区别仅在于对派生类的成员而言,基类成员对其对象的可见性与通常类及其对象的可见性相同,公有成员可见,其余成员不可见。基类的公有成员和保护成员都做为派生类的保护成员;基类的私有成员是不可见的,派生类不可访问基类中的私有成员。

  基类中的私有成员只能被基类中的成员函数和友元函数访问,不能被其余函数访问;在无继承的类中,protected与private控制符是没有差异的。在继承中,基类的private对全部的外界都屏蔽(包括本身的派生类),基类的protected控制符对应用程序是屏蔽的,但对其派生类是可访问的。

 

  每一个对象内都有一个虚表指针,指向虚函数表,虚表内存放了虚函数的地址。虚表是顺序存放虚函数地址的,不须要用到链表。

  vptr和vtble和类对象的关系:每个具备虚函数的类都有一个虚函数表vtble,里面按照在类中声明的虚函数的顺序存放着虚函数的地址,这个vtble是这个类全部对象所共有的。在每一个具备虚函数的类的对象里面都有一个vptr虚函数指针,只想这个vtalbe首地址。

  虚继承是多重继承中特有的概念。虚拟基类是为了解决多重继承而出现的。虚继承与虚函数继承是彻底不一样的概念。

  纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义本身的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”:virtual void funtion1()=0;

引入纯虚函数的缘由:

  一、为了方便使用多态特性,咱们经常须要在基类中定义虚拟函数。二、在不少状况下,基类自己生成对象是不合情理的。例如生成一我的类对象。为了解决上述问题,引入了虚函数的概念,将函数定义为纯虚函数,则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚函数的类称为抽象类,它不能生成对象。声明纯虚函数的类是一个抽象类,因此用户不能建立类的实例,只能建立它的派生类的实例。

  定义一个虚函数,不表明虚函数为不被实现的函数;定义它为虚函数是为了容许用基类的指针来调用子类的这个函数。定义一个函数为纯虚函数,才表明函数没有被实现。规范继承这个类的程序员必须实现这个函数。定义纯虚函数的目的在于,使得派生类仅仅只是继承函数的接口。

  在有动态分配堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚的;虚函数是c++中用于实现多态的机制。核心理念就是经过基类访问派生类定义的函数。

  友元函数不是成员函数,只有成员函数才能够是虚拟的,所以友元不能是虚拟函数。可是能够经过让友元函数调用虚拟成员函数来解决友元的虚拟问题。

  析构函数应当是虚函数,将调用相应对象类型的析构函数,所以若是指针指向的是子类对象,将调用子类的析构函数,而后自动调用基类的析构函数。

RTTI字面上理解就是执行时期的类型信息,其重要做用是动态判别执行时期的类型。由于虚函数有自己的局限性,当设计类别阶层时,须要判断某个对象所属的类别,而由于类别设计中大量使用了虚函数,因此使得这一工做难以实现,但又极其重要,因而使用RTTI的typrid运算符能使程序员肯定对象的动态类型。

 

中缀表达式转后缀表达式:

  规则:从左到右边遍历中缀表达式的每一个数字和符号,如果数字就输出,即成为后缀表达式的一部分;如果符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

相关文章
相关标签/搜索