该文章的要感谢博主:csdn_chai
本文总结一下C++面试时常遇到的问题。C++面试中,主要涉及的考点有
- 关键字极其用法,常考的关键字有const, sizeof, typedef, inline, static, extern, new, delete等等
- 语法问题
- 类型转换
- 指针以及指针和引用的区别
- 面向对象的相关问题,如虚函数机制等
- 泛型编程的相关问题,如模板和函数的区别等
- 内存管理,如字节对齐(内存对齐)、动态内存管理、内存泄漏等
- 编译和链接
- 实现函数和类
零、序章
0.1 C++与C的对比
- C++有三种编程方式:过程性,面向对象,泛型编程。
- C++函数符号由 函数名+参数类型 组成,C只有函数名。所以,C没有函数重载的概念。
- C++ 在 C的基础上增加了封装、继承、多态的概念
- C++增加了泛型编程
- C++增加了异常处理,C没有异常处理
- C++增加了bool型
- C++允许无名的函数形参(如果这个形参没有被用到的话)
- C允许main函数调用自己
- C++支持默认参数,C不支持
- C语言中,局部变量必须在函数开头定义,不允许类似for(int a = 0; ;;)这种定义方法。
- C++增加了引用
- C允许变长数组,C++不允许
- C中函数原型可选,C++中在调用之前必须声明函数原型
- C++增加了STL标准模板库来支持数据结构和算法
一、重要的关键字极其用法
1.1 const
主要用法
C++ 的const关键字的作用有很多,几乎无处不在,面试中往往会问“说一说const有哪些用法”。下面是一些常见的const用法的总结:
除此以外,const的用法还有:
- const引用可以引用右值,如const int& a = 1;
注:
- const 成员方法本质上是使得this指针是指向const对象的指针,所以在const方法内,
- const 成员函数可以被非const和const对象调用,而const对象只能调用const 成员函数。原因得从C++底层找,C++方法调用时,会传一个隐形的this参数(本质上是对象的地址,形参名为this)进去,所有成员方法的第一个参数是this隐形指针。const成员函数的this指针是指向const对象的const指针,当非const对象调用const方法时,实参this指针的类型是非const对象的const指针,赋给const对象的const指针没有问题;但是如果const对象调用非const方法,此时实参this指针是指向const对象的const指针,无法赋给非const对象的const指针,所以无法调用。注意this实参是放在ecx寄存器中,而不是压入栈中,这是this的特殊之处。在类的非成员函数中如果要用到类的成员变量,就可以通过访问ecx寄存器来得到指向对象的this指针,然后再通过this指针加上成员变量的偏移量来找到相应的成员变量。文章来源http://blog.csdn.net/starlee/article/details/2062586/
- const 指针、指向const的指针和指向const的const指针,涉及到const的特性“const左效、最左右效”
- const 全局变量有内部链接性,即不同的文件可以定义不同的同名const全局变量,使用extern定义可以消除内部链接性,称为类似全局变量,如extern const int a = 10.另一个文件使用extern const int a; 来引用。而且编译器会在编译时,将const变量替换为它的值,类似define那样。
const 常量和define 的区别
- const常量有数据类型,而宏定义没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意想不到的错误(边际效应)。
- 有些集成化的调试工具可以对const常量进行调试,但是不能对宏定义进行调试。
- 在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
- 内存空间的分配上。define进行宏定义的时候,不会分配内存空间,编译时会在main函数里进行替换,只是单纯的替换,不会进行任何检查,比如类型,语句结构等,即宏定义常量只是纯粹的置放关系,如#define null 0;编译器在遇到null时总是用0代替null它没有数据类型.而const定义的常量具有数据类型,定义数据类型的常量便于编译器进行数据检查,使程序可能出现错误进行排查,所以const与define之间的区别在于const定义常量排除了程序之间的不安全性.
- const常量存在于程序的数据段,#define常量存在于程序的代码段
- const常量存在“常量折叠”,在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表,可以算作一种编译优化。因为编译器在优化的过程中,会把碰见的const全部以内容替换掉,类似宏。
1.2 sizeof
- sizeof关键字不会计算表达式的值,而只会根据类型推断大小。
- sizeof() 的括号可以省略, 如 sizeof a ;
- 类A的大小是 所有非静态成员变量大小之和+虚函数指针大小
1.3 static
static的用法有:
(1)声明静态全局变量,如static int a; 静态全局变量的特点:
- 该变量在全局数据区分配内存;
- 未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化);
- 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;
(2)声明静态局部变量,即在函数内部声明的,静态局部变量的特点:
- 该变量在全局数据区分配内存;
- 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
- 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
- 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
(3)声明静态函数,限定函数的局部访问性,仅在文件内部可见
(4)类的静态数据成员,与全局变量相比,静态数据成员的好处有:
- 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;
- 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;
(5)类的静态方法
1.4 typedef
typedef 用来定义新的类型,类似的还有#define 和 using (C++11) (应该尽可能用using ,比如 using AAA = int64_t; )
与宏定义的对比
- #define 在预处理阶段进行简单替换,不做类型检查; typedef在编译阶段处理,在作用域内给类型一个别名。
- typedef 是一个语句,结尾有分号;#define是一个宏指令,结尾没有分号
- typedef int* pInt; 和 #define pInt int* 不等价,前者定义 pInt a, b;会定义两个指针,后者是一个指针,一个int。
1.5 inline
inline用来向编译器请求声明为内联函数,编译器有权拒绝。
与宏函数的对比
- 内联函数在运行时可调试,而宏定义不可以;
- 编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
- 内联函数可以访问类的成员变量,宏定义则不能;
- 在类中声明同时定义的成员函数,自动转化为内联函数
- 宏只是预定义的函数,在编译阶段不进行类型安全性检查,在编译的时候将对应函数用宏命令替换。对程序性能无影响
不能声明为inline的函数
- 包含了递归、循环等结构的函数一般不会被内联。
- 虚拟函数一般不会内联,但是如果编译器能在编译时确定具体的调用函数,那么仍然会就地展开该函数。
- 如果通过函数指针调用内联函数,那么该函数将不会内联而是通过call进行调用。
- 构造和析构函数一般会生成大量代码,因此一般也不适合内联。
- 如果内联函数调用了其他函数也不会被内联。
1.6 static const \ const \ static
1. static const
static const 数据成员可以在类内初始化 也可以在类外,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化
2. static
static数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
3. const
const数据成员只能在构造函数的初始化列表中初始化;
1.7 explicit
explicit禁止了隐式转换类型,用来修饰构造函数。原则上应该在所有的构造函数前加explicit关键字,当你有心利用隐式转换的时候再去解除explicit,这样可以大大减少错误的发生。如果一个构造函数 Foo(int) ;则下面的语句是合法的:
Foo f;
f = 12; // 发生了隐式转换,先调用Foo(int)用12构建了一个临时对象,然后调用赋值运算符复制到 f 中
如果给构造函数加了explicit,即 explicit Foo(int);就只能进行显示转换,无法进行隐式转换了:
f = 12; // 非法,隐式转换
f = Foo(12); // 合法,显示转换
f = (Foo)12;//合法,显示转换,C风格
1.8 extern
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。