【嵌入式八股3】C++:内存管理与指针
1. static 关键字的作用及与 C 的区别
static 关键字在 C++ 中主要影响变量或函数的 生命周期、作用域 和 存储位置。其具体影响如下:
1.1 修饰局部变量
- 存储位置:从 栈区 转移到 静态区。
- 生命周期:从局部生命周期变为全局生命周期。
- 作用域:作用域保持为局部范围,即函数内或代码块内。
1.2 修饰模块内的全局变量(静态全局变量)
- 存储位置:存储于 全局数据区的静态区。
- 作用域:作用域从 整个程序 限制为 当前文件。即使使用
extern声明,也无法访问。 - 生命周期:生命周期保持为程序执行期间。
1.3 修饰函数
- 作用域:函数的作用域从 整个程序 限制为 当前文件。即使使用
extern声明,也无法访问。
1.4 修饰 C++ 成员变量
- 类外定义与初始化:例如,
int A::_count = 0;,类内声明为static int _count;。 - 共享性:该静态成员变量为该类的所有对象所共享。
- 访问:通过
类名::成员变量名进行访问。
1.5 修饰 C++ 成员函数
- 无
this指针:静态成员函数没有隐式的this指针。 - 访问限制:不能访问非静态成员(变量或函数)。
- 调用限制:静态成员函数不能调用非静态成员函数,但非静态成员函数可以调用静态成员函数。
2. 指针与引用的区别
2.1 定义区别
- 指针:指向一个对象,可以间接操作该对象。
- 引用:目标变量的别名,可以直接操作。
int a = 996;
int *p = &a; // p是指针,&获取地址
int &r = a; // r是引用,&仅为标识符
2.2 主要区别
- 初始化:引用必须初始化,而指针不必。
- 修改:引用初始化后不可修改所引用的对象,而指针可以修改所指向的对象。
- 递增操作:指针递增操作是改变地址,引用递增操作是改变值。
sizeof:指针的sizeof是指针的大小,引用的sizeof是数据的大小。
2.3 类型转换
- 指针转换为引用:通过
*p进行解引用,作为参数传入即可。 - 引用转换为指针:通过
&取地址即可。
3. 深拷贝与浅拷贝
3.1 浅拷贝
- 浅拷贝是指增加了一个指向相同堆区的指针,这会导致在析构时重复释放相同的内存。
- 默认的拷贝构造和赋值运算符通常执行的是浅拷贝。
3.2 深拷贝
- 深拷贝是指在拷贝时,分配新的内存空间,并将内容复制到新的内存中。这样避免了在析构时重复释放内存的问题。
4. new 与 malloc 的区别
| 语法 | int *p = new int(0) 或 int *p = new int |
int *p = (int*)malloc(sizeof(int)) |
| 初始化 | 可以初始化 | 不会初始化 |
| 函数与运算符 | 操作符,返回指定类型的地址,无需类型转换 | 函数,返回 void*,需要类型转换 |
| 失败返回值 | 抛出异常 bad_alloc |
返回 NULL |
| 构造析构调用 | 自动调用构造函数和析构函数 | 不会调用构造析构函数 |
5. malloc 是否可以为类对象分配内存?
malloc 分配内存不会调用类的构造函数,因此不能正确初始化类对象的成员。为了正确初始化对象,应该使用 new。
6. new 与 delete 的实现
new 和 delete 实际上是对 malloc 和 free 的封装。区别如下:
- 申请失败时,
new会抛出bad_alloc异常,而malloc返回NULL。 - 对于内置数据类型,二者行为相同。对于类类型,
new会调用构造函数,delete会调用析构函数。 new通常调用系统调用brk()和mmap()。
7. 如何避免内存泄漏?智能指针的使用与原理
7.1 内存泄漏问题
C++ 不提供自动垃圾回收机制,程序员必须手动释放通过 new 分配的内存。错误的内存管理可能导致内存泄漏。
7.2 智能指针的种类
shared_ptr:使用引用计数来管理资源。引用计数递增和递减,计数为 0 时释放资源。unique_ptr:独占式智能指针,不允许复制或共享。线程安全。weak_ptr:解决循环引用问题。weak_ptr不参与引用计数,因此不会影响对象的生命周期。
7.3 智能指针的实现示例
#include <iostream>
#include <memory> // 引入智能指针头文件
class A {
public:
A(int count) : _nCount(count) {}
~A() {}
void Print() {
std::cout << "count: " << _nCount << std::endl;
}
private:
int _nCount;
};
int main() {
std::shared_ptr<A> p = std::make_shared<A>(10); // 创建智能指针
p->Print();
return 0;
}
7.4 shared_ptr 创建方式
std::shared_ptr<int> p = std::make_shared<int>(100); // 推荐使用
std::shared_ptr<int> p{new int(100)}; // 第二种创建方式
7.5 unique_ptr 示例
std::unique_ptr<int> p = std::make_unique<int>(100); // 独占指针
std::unique_ptr<int> p1(p.release()); // 转移所有权到 p1
std::unique_ptr<int> p1 = std::move(p); // 使用 std::move 转移所有权
8. shared_ptr 在多线程中的安全性
- 多个线程共享
shared_ptr的读取是安全的。 - 多个线程写同一个
shared_ptr不安全,因为引用计数可能会被同时修改。 - 多个
shared_ptr在不同线程中操作是安全的,但是对引用计数的修改不应同时发生。
9. 内存泄漏检测
9.1 定义
内存泄漏(Memory Leak)指程序中分配的堆内存没有被释放或无法被释放,导致系统内存的浪费,可能引发程序崩溃或性能下降。
9.2 避免内存泄漏的方法
- 采用良好的编码习惯,确保及时释放动态分配的内存。
- 使用智能指针来自动管理内存。
- 使用静态分析工具、源代码插装技术等进行内存泄漏检测。
嵌入式八股/模拟面试拷打 文章被收录于专栏
一些八股模拟拷打Point,万一有点用呢

查看15道真题和解析