面经汇总 C++索引

面经汇总 C++索引

Posted by zhaostu4 on November 28, 2019

T:2019/11/28 W:四 17:0:11 [HTML]: @TOC

关键字

static 关键字

  • 可以分为如下五种类型
    1. 全局静态变量
    • 在全局变量前加上关键字static,全局变量就定义成一个全局静态变量.
    • 内存中的位置:静态存储区,在整个程序运行期间一直存在。
    • 初始化:未经初始化的全局静态变量会被自动初始化为0(对于自动对象,如果没有显示初始化,会调用零参数构造函数,如不存在则编译失败);
    • 作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
      1. 局部静态变量
    • 在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。
    • 内存中的位置:静态存储区
    • 初始化:未经初始化的全局静态变量会被自动初始化为0(对于自动对象,如果没有显示初始化,会调用零参数构造函数,如不存在则编译失败);
    • 作用域:作用域仍为局部作用域,
      • 当定义它的函数或者语句块结束的时候,作用域结束。
      • 但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;

    • C语言中局部静态变量不能使用变量进行初始化的, 因为它在编译期间就被编译器初始化了.
    • C++因为引入了对象,必须调用构造函数,所以编译器把对局部静态变量的初始化推迟至第一次调用之前,也就是可以使用变量进行初始化.

      3. 静态函数   - 在函数返回类型前加static,函数就定义为静态函数。**函数的定义和声明在默认情况下都是extern的**,但**静态函数只是在声明他的文件当中可见,不能被其他文件所用**。   - 函数的实现使用static修饰,**那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;**   - warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;
      4. 类的静态成员   - 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。   - 因此,**静态成员是类的所有对象中共享的成员,而不是某个对象的成员**。对多个对象来说,静态数据成员只存储一处,供所有对象共用
      5. 类的静态函数   - **静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员**。因此,对静态成员的引用不需要用对象名。   - **在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)**。*如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);*   - 不能被virtual修饰,静态成员函数没有this 指针,虚函数的实现是为每一个对象分配一个vptr 指针,而vptr 是通过this 指针调用的,所以不能为virtual;虚函数的调用关系,this->vptr->ctable->virtual function
    
  • 说一下static关键字的作用

const 关键字

  • 一般可以分为如下六种类型
    • const变量: 表明标了为const类型, 通常需要被初始化否则后面将不能被修改, 对该变量的修改操作都会被编译器阻止.
    • const指针对象: 标明该指针为普通的左值类型可以进行修改, 但是不能通过该变量修改做指向的对象, 则通过该指针只能访问const类型的成员函数.
    • const引用: 它所绑定的对象不能被修改
    • const形参: 和普通的实参分类一样分为const 变量, const指针对象, const 引用, 作用也类似,表示不能修改该变量.
    • const返回值: 通常是为了表明返回值是一个const类型防止返回值被修改, 或则被当做左值放在赋值运算的左边
    • const成员函数: 是指成员函数不会修改类对象的任何成员变量, 如果返回值为对象成员的引用则必须返回const引用, 同时const成员函数不能调用非const函数, 其主要是因为const成员函数所持有的this指针是一个const类型的指针, 因为不能调用非const类型的成员函数,
  • 7、const?

malloc/free, new/delete 和 new[]/delete[]

  • malloc/free, new/delete的区别
    • 首先: new/deletec++关键字, 而malloc/freec语言的库函数
    • 其次:
      • malloc/free需要指明申请的空间的大小, 且并不负责构造和析构
      • new/delete是在malloc/free上增加了一层封装, 他们根据类的确定所需要的内存大小并调用malloc分配空间, 还负责调用构造和析构函数

  • 因此可以看出:
    • new operator 可以分解operator newplacement new 两个动作,是operator newplacement new 的结合。
  • new 对应的delete 没有placement delete 语法
    • 它只有两种,分别是delete operatoroperator delete
    • delete operatornew operator 对应,完成析构对象释放内存的操作。
    • operator delete 只是用于内存的释放,与C语言中的free 相似。

  • new/deletenew[]/delete[]的区别
    • new/delete
      • new
        • 简单类型直接调用operator new 分配内存;
        • 对于复杂结构,先调用operator new 分配内存,然后调用placement new在分配的内存上调用构造函数;
      • delete
        • 简单数据类型默认只是调用operator delete 释放内存;
        • 复杂数据类型先调用析构函数再调用operator delete
    • new[]/delete[]
      • new[]
        • 对于简单类型,new[]计算好大小后调用operator new
        • 对于复杂数据结构
          • AA* P = new AA[10];
          • new[]先调用operator new[]分配内存, 分配内存时多分配四个字节用于存放元素个数., 返回地址为p
          • p的最开始的4个字节用于存放元素个数n, 然后从调用n次构造函数从p-4开始构造对象.
          • 返回地址,也就是P, 即为p-4
      • delete[]
        • 对于简单类型, 直接调用operator delete进行释放(注意简单类型并没有利用4个字节保存元素个数, 由编译器自行优化)
        • 对于复制类型,
          • 首先将指针前移4个字节获得元素个数n, 然后执行n次析构函数, 最后并释放掉内存.
          • 因为指针指向的是p-4并不是内存的起始地址, 所以使用delete将无法完成释放, 因为free需要通过起始地址进行释放, 而p-4不是起始地址
  • new和delete的实现原理,delete[]是如何知道释放内存的大小的

extern “c”

  • C++调用C函数需要extern C, 因为C语言没有函数重载
    • 由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中, 而不仅仅是函数名;
    • C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。
  • 说一下extern "C"

四种cast转换

C++中四种类型转换是:static_cast, dynamic_cast, const_cast, reinterpret_cast

  • const_cast:
    • 用来移除constvolatile 属性。但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用
    • 如果对一个指向常量的指针,通过const_cast移除const属性, 然后进行修改, 编译通过,但是运行时会报段错误
  • static_cast: 静态类型转换(不能移除const/volatile属性)是最常看到的类型转换, 几个功能.
    • 内置类型之间的转换, 精度耗损需要有程序员把握
    • 继承体系中的上下行转换(上行:子类转父类,安全转换; 下行:父类转子类, 不安全转换)
    • 指针类型转换: 空指针转换成目标类型的空指针, 把任何类型转换成void 类型
  • dynamic_cast: 主要用在继承体系中的安全向下转型
    • 它能安全地将指向基类的指针/引用转型为指向子类的指针/引用, 转型失败会返回null(转型对象为指针时)或抛出异常bad_cast(转型对象为引用时)。
    • dynamic_cast 会利用运行时的信息(RTTI)来进行动态类型检查,因此dynamic_cast 存在一定的效率损失。
    • 而且dynamic_cast进行动态类型检查时, 利用了虚表中的信息, 所以只能用于函数虚函数的类对象中.
  • reinterpret_cast 强制类型转换,非常不安全

++i 和 ++i

  • ++i 和 ++i的区别
    • ++i先自增1, 再返回. 函数定义式无形参, 实现过程中不会有临时变量生成,
    • i++先返回i,再增加1. 函数定义式有一形参, 实现过程中由于需要返回自增前的量,所以需要一个临时变量.
  • ++i 和 ++i的区别
  • ++i 和 i++的实现

class, union, struct

  • structCC++中是不同的
    • C语言中:
      • struct为自定义数据类型, 结构体名不能单独作为类型使用, 其结构名前必须加struct 才行
      • struct为变量的集合, 不能存定义函数(但是可以存在函数指针变量)
      • struct不存在访问权限控制的概念
    • C++中:
      • struct为抽象数据类型, 只一个特殊的class, 支持成员函数的定义, 可以继承和实现多态
      • 增加了访问权限控制的概念, 但是默认访问和继承权限为public
      • 结构体名字可以为直接做为类型使用
  • C++structclass的区别
    • 默认的访问和继承权限不同
    • 注意C++struct可以使用模板
  • union联合
    • C语言中:
      • union是一种数据格式,能够存储不同的数据类型,但只能同时存储其中的一种类型。
      • union的数据成员是共享内存的, 以成员最大的做为结构体的大小
      • 每个数据成员在内存中的起始地址是相同的。
    • C++中:
      • union 结构式一种特殊的类。 默认访问权限是public
      • 能包含访问权限、成员变量、成员函数(可以包含构造函数和析构函数)。
      • 不能包含虚函数和静态数据变量。也不能被用作其他类的基类,它本身也不能从某个基类派生而来。
      • union成员是共享内存的,以size 最大的结构作为自己的大小。
      • 每个数据成员在内存中的起始地址是相同的。
    • 无论是C/C++, union的储存方式都是小端模式储存的
  • class、union、struct 的区别

mutable

  • 通常情况下
    • const成员函数时不能被类对象的成员变量的, 但是可以修改被mutable修饰的成员变量
    • 通常我们任务mutable位类的辅助状态, 只是类的一些表诉功能, 修改它不会改变对象的状态
    • 通常我们可以是用const_castconst成员函数中修改所有的成员变量
  • mutable

volatile

  • volatile 关键字是一种类型修饰符,被它修饰的变量拥有三大特性: 易变性, 不可优化性, 顺序性
    • 易变性: 编译器对valatile的访问总是从内存中读取数据, 即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
    • 不可优化性: volatile告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。
    • 顺序性: 保证Volatile变量间的顺序性,编译器不会进行乱序优化。但是可能会被CPU优化
  • 声明时语法:int volatile vInt;
  • volatile 用在如下的几个地方: 1) 中断服务程序中修改的供其它程序检测的变量需要加volatile; 2) 多任务环境下各任务间共享的标志应该加volatile; 3) 存储器映射的硬件寄存器通常也要加volatile 说明,因为每次对它的读写都可能由不同意义;
  • 113、volatile 关键字的作用?

define, const, typedef, inline

  • const#define 的区别:作用阶段不同, 功能不同, define作用丰富, 占用的空间不同, 作用域
    • 作用阶段不同: const在编译和链接阶段其作用, define在预编译阶段起作用
    • 功能不同:
      • const是定义一个变量, 拥有数据类型, 会进行语义语法检查
      • define是宏定义, 简单的问题替代, 没有类型检查
    • define的作用更丰富: define可以配合条件预编译指令, 完成特殊的逻辑, 例如防止重复引用文件
    • 编译后占用的空间: const定义的是变量, 会储存在数据段空间, define是宏替换, 其值会储存在代码段
    • 作用域不同: define没有作用域限制, 而const定义的变量通常有作用域的限制(全局变量默认为extern)
  • #define 和别名typedef 的区别 1) 执行时间不同,typedef 在编译阶段有效,typedef 有类型检查的功能;#define 是宏定义,发生在预处理阶段,不进行类型检查; 1) 功能差异,typedef 用来定义类型的别名,定义与平台无关的数据类型,与struct的结合使用等。#define 不只是可以为类型取别名,还可以定义常量、变量、编译开关等。 1) 作用域不同,#define 没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef 有自己的作用域。

  • defineinline 的区别 1) #define 是关键字,inline 是函数; 1) 宏定义在预处理阶段进行文本替换,inline 函数在编译阶段进行替换; 1) inline 函数有类型检查,相比宏定义比较安全;
  • 81、defineconsttypedefinline 使用方法?

ifdef, endif

  • 从源文件到可执行程序的过程, 通常要经历: 预编译, 编译, 汇编, 链接等过程
  • ifdef, endif为条件预编译指令, 生效于预编译阶段, 根据条件可以完成一些特殊的逻辑, 例如防止文件重复引用
  • #ifdef, #else,#endif为完整的逻辑, 分别表示, 如果定义了某个标识符, 则编译后续程序段, 否则编译另外一个程序段
  • 因为预编译阶段处于编译链的第一阶段, 它可以直接影响应用程序的大小.
  • 96、说一下理解ifdef endif

cout

assert 和 NDEBUG

Debug 和 release

函数

fork, wait, exec

strcpy 和 strlen

strcpy 和 strncpy

memset

main函数

特性

C和C++的区别

野指针

指针和引用的区别

指针和引用的选用

数组和指针的区别

函数的实参和形参的传递方式

函数调用过程

静态变量初始化时机

模板的实现

多态,重载,重写/覆盖,隐藏,模板

左右值, 左值引用和右值引用

内存对齐

位域

lambda函数

重载运算符

隐式变换

大小端

共享内存

reactor模型

编译和底层

段错误

内存泄漏

C/C++内存布局

C++函数栈空间

动态联编和静态联编

动态编译和静态编译

动态链接和静态链接

include引用中”“和<>的区别

从源代码到可执行文件的过程

malloc的原理

类和数据抽象

类的访问权限, 类的的继承权限

static, inline和virtual关键字

类与类之间的关系 继承, 包含 和 使用

RTTI

静态成员函数和虚函数的区别

成员初始化列表

构造函数与虚函数

构造函数扩展和执行顺序

默认构造函数

析构函数与虚函数

析构函数扩展和执行顺序

构造函数/析构函数和异常

默认析构函数

类对象的静态分配/动态分配

STL

STL的基本组成

ATL的allocator

迭代器的作用,有了指针为什么还要有迭代器

map和set的实现

map, multimap, unorderd_map

vector和list的区别