十七
十七
Published on 2025-03-04 / 17 Visits
0
0

C++八股文(一)基础知识

1. 命名空间

1.1 使用方式:

  • 作用域限定符:wd::number

  • using 编译指令:using namespace std;

问题:using编译指令可能会造成冲突,这是因为using编译指令它会把该空间中的所有实体一次性全部引入

  • using声明机制:using std::cout; // 只会引入这一个实体,推荐使用

1.2匿名的命名空间

  • 什么是模块?一个*.c/*.cc/*.cpp的文件就可以称为一个模块

  • 只能在本模块内部使用的是什么?不能跨模块使用的是什么?

C语言中,static变量 static函数都只能在本模块内部使用,外部无法引入

C++中,匿名命名空间的实体也是只能在本模块内部使用

全局变量就可以跨模块调用 extern关键词

  • 补充static知识

修饰变量

普通局部变量(存储于进程栈空间,使用完会立即释放):在任何一个函数内部定义的变量都属于这个范畴。编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值

静态局部变量(存储于进程的静态数据区,即使函数返回,它的值也会保持不变):使用static修饰符定义的局部变量,即使在声明时未赋初值。编译器也会把它初始化为0

普通全局变量:对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其名字相同的变量了(负责编译器会认为他们是同一个变量)

静态全局变量:仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用

修饰函数

函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数

静态成员函数只能使用静态数据成员

  • 命名空间是否只可以定义一次?

函数声明可以有多次,但定义只有一次

同一个模块中,可以定义多次命名空间;

在不同的模块中,也可以定义多次命名空间

命名空间就像一个容器(黑洞)可以无限定义实体

2.const关键字

  • 修饰类型: char/short/int/long

const int b = 2;// 不能再修改的值,常量

  • 宏定义与const常量有什么区别?

发生的时机:宏定义是在预处理时;const常量是在编译时

类型检查:宏定义没有类型检测,只是简单做了字符串的替换,虽然也有编译阶段,但在编译阶段没有报错,将出错的时机延迟到了运行时。运行时的错误更难发现;const是有类型检查的,更安全一些。

  • const修饰指针

常量指针(pointer to const):const int *p1 = &a; int const *p2 = &a;

不能修改的是指针所指空间的值

所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。

可以这样想:所谓指向常量的指针和引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,所以自觉的不去改变所指对象的值。

指针常量(const pointer):int *const p3 = &a;

不能修改指针变量的指向

3、new/delete表达式

3.1 C语言中动态申请堆空间的资源,采用的是malloc/calloc

3.2 C++中也提供了类似的方式,new表达式/delete表达式

  • int *pint = new int(1);// new表达式申请空间的同时,也进行了初始化

  • delete pint; // 释放堆空间

  • int *parr = new int[10];

  • delete [] parr;

3.3 malloc的底层实现?new表达式底层实现?

  • free(p);// 如何判断该回收多少空间?

在C中,free 函数并不需要显式地知道要释放多少空间。当你调用 free(ptr) 时,ptr 是一个指向动态分配内存块的指针,free 将释放 ptr 指向的整个内存块。

这是因为malloc在内部通常会在分配的内存块的起始部分存储一些额外的信息,其中包括分配的大小。free则使用这个大小信息来确定要释放的空间

  • malloc 使用了一种内存池(memory pool)的机制

在程序启动时,malloc 会初始化一个内存池。这个内存池一般由一个或多个堆(heap)组成。

堆是进程内的一块大内存区域,由操作系统分配给进程。malloc 使用堆来存储动态分配的内存块。

当调用 malloc(size) 时,malloc 会在内存池中寻找一个足够大的、未被使用的内存块。

如果找到了足够大的内存块,malloc 将这个内存块标记为已分配,并返回一个指向这个内存块的指针。

这个过程可能涉及内存块的拆分(splitting)或合并(coalescing),以便更好地利用内存。

3.4 malloc/free 与new/delete表达式的区别是什么?

  • malloc和free是C语言的函数,它们只关心分配和释放内存,不涉及构造和析构对象

  • new和delete是C++的运算符,除了分配和释放内存外,它们还会调用对象的构造函数和析构函数,处理对象的生命周期

  • new能够自动分配空间大小,malloc需要传入参数;

  • new开辟空间的同时还对空间做了初始化的操作,而malloc不行;

3.5 内存泄漏工具valgrind的使用

命令较长,可以起一个别名放在~/.bashrc文件中:alias memcheck='valgrind --tool=memcheck --leak-check=full'

接下来再执行source.bashrc

3.6 new 表达式工作步骤

  1. 调用名为operator new的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象:void* operator new(size_t size){ return malloc(size)};

  2. 运行该类型的一个构造函数初始化对象

  3. 返回指向新分配的构造函数对象的指针

3.7 delete 表达式工作步骤

  1. 调用析构函数,回收对象中数据成员所申请的资源,也就是malloc申请的堆空间

  2. 调用名为operator delete的标准库函数释放该对象所用的内存

4. &符号的理解

  1. 引用&:对变量取一个别名

int a = 1; int & ra = a;

  1. 取地址 &:int a = 1; int *p = &a;

  2. 按位与 &:int c = a & b;

  3. 引用可以作为函数的参数:

经典例子,交换两个变量的值

对比使用指针的好处是: 没有赋值的开销,可以提高程序的执行效率

引用的底层实现其实还是指针,而且是一个受限制的指针

  1. 引用作为函数的返回值

类型的变量:在return语句中直接返回一个变量,要执行的是复制

指针类型:只做了一个地址传递,效率很高,也是进行了复制的

引用类型:代表的就是变量本身,没有复制开销

5. 强制转换

5.1 static_cast

常见的指针转换(把void *转换成其他类型的指针) 用的最多。

5.2 const_cast

去除常量属性,用法很诡异

5.3 dynamic_cast

只用在多态时,基类与派生类之间的转换

5.4 reinterpret_cast

不要轻易使用,在任意类型之间进行转换,平时用的最少

6.函数重载

6.1 C语言不支持函数重载

  • C语言中如果有新的接口时,会用数字来重新命名

  • 正是因为C语言不支持函数重载,所以在C语言中,函数名就代表了函数的入口地址

  • 而C++支持函数重载,所以函数的地址最好使用函数名加上取地址符号&

6.2 什么叫函数重载(overload)?

函数名相同,但参数列表不同:函数参数的类型、个数、顺序不同(没有函数返回值类型)

6.3 C++实现函数重载

  • 实现原理: 名字改变(name mangling)

  • 具体步骤: 当函数名相同时,会根据函数参数的类型、个数、顺序不同进行改变

  • 验证:g++ -c overload.cc 得到overload.o文件,使用nm overload.o命令查看

7. C语言和C++的混合编程

7.1 背景

  • C语言写的库已经遍地开花,C++需要调用C的库,必须要能按照C的方式进行调用。

  • 希望使用C++的编译器来编译C的源码

7.2 解决方案

extern "c"{}

只要放在该区域的代码,都会按照C的方式进行调用,不会进行名字改编

搭配宏可以完美解决

#ifndef __ADD_H__
#define __ADD_H__

#ifdef __cplusplus
extern "C"
{
#endif
int add(int x, int y )
{
    return x + y;
}
#ifdef __cplusplus
}
#endif

#endif

8.内存分布

这里面有错误,char char2[] = "abcd"; 这里的"字符串不是字面值常量",不是放在代码段的,而是放在栈中的(可以进行修改)


Comment