本文经自动驾驶之心公众号授权转载,转载请联系出处。
1. 开始
本文目的是整理面试常见的会问到的题目, 具体细节的学习需要参考 C++ Primer / Effective C++ 系列书籍 / Inside the C++ Object Model 进行学习.
为了方便查阅, 补充了可能没有面试内容的一级标题. 这样一级标题可以和 C++ Primer 书籍保持一致.
1.1. C 和 C++ 的区别
设计思想上:
- C++ 是面向对象的语言, C 是面向过程的语言
语法上:
- C++ 具有封装/继承/多态三种特性.
- C++ 相比 C, 增加了类型安全的功能, 比如强制类型转换.
- C++ 支持范式编程, 比如模板类/函数模板等.
2. 变量和基本类型
2.1. 复合类型
复合类型(compound type)是指基于其他类型定义的类型. 最常见的是引用和指针.
引用即别名: 引用(reference)为对象起了另外一个名字, 引用类型引用(refers to)另外一种类型.
- 定义引用时, 程序把引用和它的初始值绑定在一起, 而不是将初始值拷贝给引用. 一旦初始化完成, 引用将和它的初始值对象一直绑定在一起. 因为无法令引用重新绑定到另外一个对象, 因此引用必须初始化.
- 因为引用不是一个对象, 所以不能定义引用的引用.
指针(pointer)是指向(point to)另外一种类型的复合类型.
- 指针无需在定义时赋初值.
- 指针本身就是一个对象, 允许对指针赋值和拷贝, 而且在指针的生命周期内它可以先后指向几个不同的对象.
表 2.1 指针与数组的区别
2.2. const限定符
2.2.1. 作用
- 修饰变量: 表明该变量的值不可以被改变.
- 修饰指针: 区分指向常量的指针和常量指针.
- 修饰引用: 用于形参, 既避免了拷贝, 又避免了函数对值的修改.
- 修饰成员函数: 表示函数不能修改成员变量(实际上是修饰this指针)
补充:
- 对于局部对象,常量存放在栈区;
- 对于全局对象, 常量存放在全局/静态存储区;
- 对于字面值常量, 常量存放在常量存储区(代码段).
2.2.2. 指向常量的指针 VS 常量指针
参考 C++ Primer 2.4.2 指针和const
:
- 指向常量的指针(pointer to const):
- 具有只能够读取内存中数据, 却不能够修改内存中数据的属性的指针(底层 const).
const int * p
;或者int const * p
;- 常量指针(const pointer): 常量指针是指指针所指向的位置不能改变, 即指针本身是一个常量(顶层 const), 但是指针所指向的内容可以改变.
- 常量指针必须在声明的同时对其初始化, 不允许先声明一个指针常量随后再对其赋值, 这和声明一般的常量是一样的.
int * const p = &a;
2.2.3. cosntexpr
- 常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式.
- 一般来说, 如果认定变量是一个常量表达式, 那就把它声明成
constexpr
类型. - 一个
constexpr
指针的初始值必须是nullptr
或者0
, 或者是存储于某个固定地址中的对象. - constexpr函数是指能用于常量表达式的函数.
- 函数的返回类型及所有的形参的类型都得是字面值类型.
- 函数体中必须有且只有一条return语句.
2.2.4. #define VS const
3. 字符串、向量和数组
4. 表达式
4.1. 右值
C++的表达式要不然是右值(rvalue), 要不然是左值(lvalue). 这两个名词是从 C 语言继承过来的, 原本是为了帮助记忆: 左值可以位于赋值语句的左侧, 右值则不能.
当一个对象被用做右值的时候, 用的是对象的值(内容); 当对象被用做左值的时候, 用的是对象的身份(在内存中的位置).
4.2. ++i/i++
前置版本++i
: 首先将运算对象加 1, 然后将改变后的对象作为求值结果.
后置版本i++
: 也会将运算对象加 1, 但是求解结果是运算对象改变之前的那个值的副本.
以下摘录自 More Effective C++ Item 6:
// prefix form(++i): increment and fetch
UPInt& UPInt::operator++()
{
*this +=1; // increment
return *this; // fetch
}
// postfix form(i++): fetch and increment
const UPInt UPInt::operator++(int)
{
const UpInt oldValue = *this; // fetch
++(*this); // increment
return oldValue; // return what was fetched
}
4.3. sizeof运算符
4.3.1. 普通变量执行sizeof
sizeof
运算符的结果部分地依赖于其作用的类型:
- 对char或者类型为char的表达式执行sizeof运算, 结果得 1.
- 对引用类型执行sizeof运算得到被引用对象所占空间的大小.
- 对指针执行sizeof运算得到指针本身所占空间的大小.
- 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小.
- 对数组执行sizeof运算得到整个数组所占空间的大小, 等价于对数组中所有元素各执行一次sizeof运算并将所得结果求和.
- 对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小.
4.3.2. 类执行sizeof
class A {};
class B { B(); ~B() {} };
class C { C(); virtual ~C() {} };
class D { D(); ~D() {} int d; };
class E { E(); ~E() {} static int e; };
int main(int argc, char* argv[]) {
std::cout