C++ 面试知识点总结

记录刷题的细节内容,算法与 C++ 特性(等之后有空再优化)。


static 定义静态局部变量特点

  • 在全局数据区分配内存
  • 首次初始化执行后,之后函数调用不再初始化
  • 无显式初始化时,自动初始化为 0

局部变量、全局变量与动态数据的内存区域

局部变量存在于堆栈中,全局变量存在于静态区中,动态申请的数据存在于堆中。


模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename 数据类型参数标识符>(typenameclass 没区别)
<返回类型><函数名>(参数表)
{
函数体
}
模板形参不能为空。一旦声明了模板函数就可以用模板函数的形参名
声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方
都可以使用模板形参名
example:
template<class T> struct{
static void foo(T op1, T op2){
cout<<op1<<op2;
}
};sum <int>::foo(1,3) //一个完整的例子

C++ 虚函数概念

C++ 中的虚函数的作用主要是实现了多态的机制。基类定义虚函数,子类可以重写该函数。

当子类重新定义了父类的虚函数后,当父类的指针指向子类对象的地址时,即 B b; A *a = &b;,父类指针根据赋给它的不同子类指针,动态的调用子类的该函数,而不是父类的函数。而如果使用了 virtual 关键字,程序将根据引用或指针指向的对象类型来选择方法,否则使用引用类型或指针类型来选择方法。

1
2
3
4
5
6
7
8
B b;
A *pa;
pa = &b;
A *pa2 = new A;

pa->FuncA(); // (3) pa=&b 动态绑定,但是 FuncA 不是虚函数,所以 FuncA called
pa->FuncB(); // (4) FuncB 是虚函数,所以调用 B 中 FuncB,FuncBB called
pa2->FuncA(); // (5) pa2 是 A 类指针,不涉及虚函数,调用的都是 A 中函数,所以输出 FuncA called FuncB called

vector 的函数 erase()

一次只删除一个元素,返回一个迭代器指针,指向下一个元素。


默认只读模式打开文件

ios::appios::app | ios::out 是一个意思,只能输出不能输入。


数组的定义区别理解

1
2
3
4
5
6
7
8
9
10
11
12
int *s[8];
定义一个指针数组,该数组中每个元素是一个指针,
每个指针指向哪里就需要程序中后续再定义了。
int (*s)[8];
定义一个数组指针,该指针指向含8个元素的一维数组(数组中每个元素是int型)。

区分int *p[n]; 和int (*p)[n];
就要看运算符的优先级了。
int *p[n]; 中,运算符[ ]优先级高,先与p结合成为一个数组,
再由int*说明这是一个整型指针数组。
int (*p)[n]; 中( )优先级高,
首先说明p是一个指针,指向一个整型的一维数组。

函数模板

函数模板必须由编译器根据程序员的调用类型实例化为可执行的函数。

类模板的成员函数都是函数模板。

没使用过的成员函数(即函数模板)不会被实例化。


双目运算符的判断机制

&& 和 || 的判断机制:A&&B,若 A 为假,直接返回 false,B 不执行;若 A 为 true,继续执行 B。A||B,若 A 为 true,直接返回 true,B 不执行。


C++ 中的 switch

  1. switch 后面的”表达式”,可以是 int、char 和枚举型中的一种,不能是 float 型变量

  2. case 后面必须是”常量表达式”,表达式中不能包含变量

  3. switch 后的表达式加了分号;非法


unsigned

unsigned 为无符号整数,取值范围为 0-255,永远不可能小于零,最好不要做控制条件。


派生类继承编译

1
2
3
4
5
6
7
8
派生类继承(public, private)基类成员后,
继承的基类成员在派生类中的相应权限的改变。
public 继承,基类的各类(public, protected, private)成员
在派生类中权限与原来在基类时保持一致;
private 继承,基类的各类成员在派生类中权限均变为private.
故C类中的print调用A::print(),
因为A::print()此时在C类中为private等级,
所以不能被C类任何成员所访问,故不能通过编译

引用

引用是一个对象的别名,引用类型引用(refer to)另外一种类型。如 int &refVal = val;

  • 引用必须初始化
  • 引用与其初始值是**绑定(bind)**在一起的,而不是拷贝
  • 值传递以拷贝实参的方式,传递给形参

C++ STL 的 string 库与 C 风格字符串

C++ STL(Standard Template Library)string 库中的风格比 C 风格的字符串更加安全、高效。


while 与 for

  • while: 为不确定迭代的次数时使用,例如读取数据
  • for: 确定范围时使用,也可以省略初始值、条件以及表达式

异常问题

throw、try、异常块;

throw: 异常表达式表示其遇到无法处理的问题,引发(raise)的异常

try: try 和一个或多个 catch 块配合使用,一般的 try 抛出的某个异常会被某个 catch 捕获处理,故 catch 称为异常处理代码。


class 与 struct 区别

在于访问权限

class 第一个访问说明符之前的成员为 private

struct 第一个访问说明符之前的成员为 public


使用非成员版本的 swap 是一个好习惯

swap(c1, c2)c1.swap(c2) 等价,但最好使用前者。


泛型算法

头文件 #include <algorithm>#include <numeric> 中,标准库提供了超过 100 个算法,所以说阅读源代码非常重要。

1
2
3
copy (ilst.begin(), ilst.end(), back_inserter(ivec))
copy函数


关联容器(hash、map、set)

  • mapmap<string, int> word_count = {{"a", 1}, {"b", 2}};
  • setset<string> exclude = {"the", "a"};
  • unordered_map(hash 组织 map)
  • unordered_set(hash 组织 set)

字符指针

char *str[3] = {"stra","strb","strc"}; 是一个指针数组;char *p = str[0];实际上 p 指向第一个字符串,所以字符串加 1,则指向字符串的下一个字母,而并非是下一个字符串。


static 与非 static、全局变量与局部变量

无论是 static 还是非 static 的全局变量,如果不加限制随意访问的话,容易出现同步问题。

无论是 static 还是非 static 的局部变量,每个线程都是私有的,其他线程不会对其进行干扰。


C/C++ 中 main 函数

main 函数是 C/C++ 程序的入口函数,为必须存在的函数。


throw、catch、try

  • throw 是抛出异常的关键字,try 是尝试执行可能有异常代码的关键字,catch 是捕获异常的关键字
  • 可能抛出异常的代码块都应该放在 try 代码块中
  • catch 捕获相应的异常,可以有多级 catch 代码块来捕获不同级别的异常
  • 被捕获的异常可以再次抛出

线程

线程使用了 wait 方法,会强行打断当前操作,进入阻塞(暂停)状态,然后需要 notify 方法或 notifyAll 方法才能进入就绪状态。


枚举变量

枚举变量是全局变量的情况下,枚举值的缺省值是 0,不是枚举的第一个值。其他情况,其值是不定的,而且不限定于所列出的枚举值。

全局变量时初始化为 0,局部变量时初始化为随机值。


unsigned 指针运算

1
2
3
4
5
6
7

移动的1代表的是一个单位量
unsigned
p1+5=p1+5*1=p1+5*sizeof(unsigned char)
=p1+5*1=0x801000+0x5=0x801005
p2+5=p2+5*1=p2+5*sizeof(unsigned long)
=p2+5*4=0x810000+20=0x810000+0x14=0x810014

数组、指针、字符串的长度计算

对字符串进行 sizeof 操作的时候,会把字符串的结束符 \0 计算进去的。

进行 strlen 操作求字符串的长度的时候,不计算 \0 的。

数组作为函数参数传递的时候,已经退化为指针了,Func 函数的参数 str_arg 只是表示一个指针,那个 str_arg[] 括号中的数值不起任何作用。


强制类型转换后指针的类型

1
2
3
4
5
6
7
char str[] = "glad to test something";
char *p = str;
p++;
int *p1 = reinterpret_cast<int *>(p);
p1++;
p = reinterpret_cast<char *>(p1);
printf("result is %s\n", p);

p 的类型为 char *p++p 指向 str 数组的第 2 个元素即字母 “l” 的位置。

p1 的类型为 int *p1++p1 指向的位置增加 4 个字节,指向 str 数组中的第 6 个元素即字母 t 的位置。

因此最后 p 的内容为 to test something


  • 在类中,如果为空类,则类占用 1 个字节
  • 一旦类中有其他的占用空间成员,则这 1 个字节就不在计算之内
  • 一个类只有一 int 则占用 4 字节而不是 5 字节
  • 如果只有成员函数,则还是只占用 1 个字节,因为类函数不占用空间
  • 但是由于虚函数存在一个虚函数表,需要 4 个字节,数据成员对象如果为指针则为 4 字节,注意有字节对齐,如果为 13 字节,则进位到 16 字节空间

buffer

1
2
3
4
函数char *myString()中没有使用new或者malloc分配内存,
所有buffer数组的内存区域在栈区
随着char *myString()的结束,栈区内存释放,字符数组也就不存在了,
所以会产生野指针,输出结果未知

动态分配在堆区

动态分配在堆区,其他的均不在堆区。


操作系统中用户态切换到内核态的 3 种方式

a. 系统调用

b. 异常

c. 外围设备的中断


函数嵌套

若有如下函数定义,这种嵌套定义根本算不出结果,所以函数嵌套定义肯定不行。

1
2
3
int f(int a)={return a+f1(a);}
int f1(int b)={return b+f(b);}
无法算出结果故定义嵌套不使用

静态变量初始化

在 C++ 中,类的静态成员(static member)必须在类内声明,在类外初始化。

1
2
3
4
5
class A
{
private: static int count ; // 类内声明
};
int A::count = 0 ; // 类外初始化,不必再加static关键字

为什么?因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。

能在类中初始化的成员只有一种,那就是静态常量成员。

这样不行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A
{
private: static int count = 0;
静态成员不能在类内初始化
};

class A
{
private: const int count = 0;
常量成员也不能在类内初始化
};

class A
{
private: static const int count = 0;
静态常量成员可以在类内初始化
};

C++ 面试知识点总结
https://chaggle.github.io/2020/03/24/cpp/cpp-interview-notes/
作者
chaggle
发布于
2020年3月24日
更新于
2026年8月10日
许可协议