【嵌入式八股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 主要区别

  1. 初始化:引用必须初始化,而指针不必。
  2. 修改:引用初始化后不可修改所引用的对象,而指针可以修改所指向的对象。
  3. 递增操作:指针递增操作是改变地址,引用递增操作是改变值。
  4. sizeof:指针的 sizeof 是指针的大小,引用的 sizeof 是数据的大小。

2.3 类型转换

  • 指针转换为引用:通过 *p 进行解引用,作为参数传入即可。
  • 引用转换为指针:通过 & 取地址即可。

3. 深拷贝与浅拷贝

3.1 浅拷贝

  • 浅拷贝是指增加了一个指向相同堆区的指针,这会导致在析构时重复释放相同的内存。
  • 默认的拷贝构造和赋值运算符通常执行的是浅拷贝。

3.2 深拷贝

  • 深拷贝是指在拷贝时,分配新的内存空间,并将内容复制到新的内存中。这样避免了在析构时重复释放内存的问题。

4. newmalloc 的区别

属性 newmalloc
语法 int *p = new int(0)int *p = new int int *p = (int*)malloc(sizeof(int))
初始化 可以初始化 不会初始化
函数与运算符 操作符,返回指定类型的地址,无需类型转换 函数,返回 void*,需要类型转换
失败返回值 抛出异常 bad_alloc 返回 NULL
构造析构调用 自动调用构造函数和析构函数 不会调用构造析构函数

5. malloc 是否可以为类对象分配内存?

malloc 分配内存不会调用类的构造函数,因此不能正确初始化类对象的成员。为了正确初始化对象,应该使用 new

6. newdelete 的实现

newdelete 实际上是对 mallocfree 的封装。区别如下:

  • 申请失败时,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,万一有点用呢

全部评论
指针引用区别清晰
点赞 回复 分享
发布于 2025-03-06 20:01 山东
static用法很全面
点赞 回复 分享
发布于 2025-03-03 11:24 陕西
static用法很全面
点赞 回复 分享
发布于 2025-02-28 16:13 陕西

相关推荐

狄文君:多段项目经历 + 专业技能拉满,这简历很能打,坐等好消息!
点赞 评论 收藏
分享
评论
4
11
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务