Lesson05&6 --- C&C++内存管理&模板初阶

目录

[一、C/C++ 内存管理](#一、C/C++ 内存管理)

[1.1 C/C++ 内存分布](#1.1 C/C++ 内存分布)

[1.1.1 内存区域划分(从高地址到低地址)](#1.1.1 内存区域划分(从高地址到低地址))

[1.1.2 经典真题(选择 + 填空)](#1.1.2 经典真题(选择 + 填空))

[1.2 C 语言动态内存管理(malloc/calloc/realloc/free)](#1.2 C 语言动态内存管理(malloc/calloc/realloc/free))

[1.2.1 函数用法与核心区别](#1.2.1 函数用法与核心区别)

[1.2.2 常见问题:realloc 后是否需要 free 原指针(p2)?](#1.2.2 常见问题:realloc 后是否需要 free 原指针(p2)?)

[1.3. C++ 动态内存管理(new/delete)](#1.3. C++ 动态内存管理(new/delete))

[1.3.1 操作内置类型(申请 + 初始化 + 释放)](#1.3.1 操作内置类型(申请 + 初始化 + 释放))

[1.3.2 操作自定义类型(自动调用构造 / 析构函数)](#1.3.2 操作自定义类型(自动调用构造 / 析构函数))

[1.3.3 核心注意点:new [] 与 delete [] 必须匹配](#1.3.3 核心注意点:new [] 与 delete [] 必须匹配)

[1.4 operator new 与 operator delete 函数](#1.4 operator new 与 operator delete 函数)

[1.4.1 核心本质:全局函数(new/delete 的底层依赖)](#1.4.1 核心本质:全局函数(new/delete 的底层依赖))

[1.4.2 底层实现与失败处理](#1.4.2 底层实现与失败处理)

[1.4.3 关键注意点](#1.4.3 关键注意点)

[1.5 new 和 delete 的实现原理](#1.5 new 和 delete 的实现原理)

[1.5.1 内置类型](#1.5.1 内置类型)

[1.5.2 自定义类型](#1.5.2 自定义类型)

[new 的原理(3 步)](#new 的原理(3 步))

[delete 的原理(3 步)](#delete 的原理(3 步))

[new [] 的原理(4 步)](#new [] 的原理(4 步))

[delete [] 的原理(4 步)](#delete [] 的原理(4 步))

[1.6 定位 new 表达式(placement-new)](#1.6 定位 new 表达式(placement-new))

[1.6.1 通俗概念](#1.6.1 通俗概念)

[1.6.2 用法格式与代码示例](#1.6.2 用法格式与代码示例)

[1.7 高频面试题(内存管理核心)](#1.7 高频面试题(内存管理核心))

[1.7.1 malloc/free 与 new/delete 的 7 大区别(面试必问)](#1.7.1 malloc/free 与 new/delete 的 7 大区别(面试必问))

[1.7.2 内存泄漏](#1.7.2 内存泄漏)

(1)定义

(2)危害

[(3)分类(核心关注 2 类)](#(3)分类(核心关注 2 类))

(4)检测方法

(5)避免方法(事前预防为主)

二、模板初阶(泛型编程)

[2.1 泛型编程(模板的核心目标)](#2.1 泛型编程(模板的核心目标))

[2.1.1 问题引入:函数重载的 2 大缺点](#2.1.1 问题引入:函数重载的 2 大缺点)

[2.1.2 泛型编程定义](#2.1.2 泛型编程定义)

[2.2 函数模板](#2.2 函数模板)

[2.2.1 概念与格式](#2.2.1 概念与格式)

[2.2.2 实现原理](#2.2.2 实现原理)

[2.2.3 实例化(生成具体类型函数)](#2.2.3 实例化(生成具体类型函数))

[(1)隐式实例化:编译器根据实参自动推演 T](#(1)隐式实例化:编译器根据实参自动推演 T)

[(2)显式实例化:手动指定 T 的类型](#(2)显式实例化:手动指定 T 的类型)

[2.2.4 模板参数匹配原则(3 大规则)](#2.2.4 模板参数匹配原则(3 大规则))

[规则 1:非模板函数与同名函数模板可共存](#规则 1:非模板函数与同名函数模板可共存)

[规则 2:优先调用非模板函数,模板可生成更匹配版本时选模板](#规则 2:优先调用非模板函数,模板可生成更匹配版本时选模板)

[规则 3:模板函数不支持自动类型转换,普通函数支持](#规则 3:模板函数不支持自动类型转换,普通函数支持)

[2.3 类模板](#2.3 类模板)

[2.3.1 定义格式(以动态顺序表 Vector 为例)](#2.3.1 定义格式(以动态顺序表 Vector 为例))

[2.3.2 类模板的实例化(必须显式指定类型)](#2.3.2 类模板的实例化(必须显式指定类型))

[2.3.3 关键注意点](#2.3.3 关键注意点)

三、学习总结与建议

内存管理部分

模板部分


一、C/C++ 内存管理

1.1 C/C++ 内存分布

1.1.1 内存区域划分(从高地址到低地址)

|----------|------------------------|------------------|
| 内存区域 | 存储内容 | 核心特点 |
| 内核空间 | 操作系统内核代码 / 数据 | 用户代码不可读写 |
| 栈(向下增长) | 非静态局部变量、函数参数、返回值 | 空间小(几 MB),自动分配释放 |
| 内存映射段 | 共享动态库、共享内存 | 高效 I/O 映射,进程间通信 |
| 堆(向上增长) | 动态分配的内存(malloc/new 申请) | 空间大,需手动分配释放 |
| 数据段(静态区) | 全局变量、静态变量(static) | 程序运行期间一直存在 |
| 代码段(常量区) | 可执行代码、只读常量(如字符串常量) | 只读,不可修改 |

1.1.2 经典真题(选择 + 填空)

cpp 复制代码
int globalVar = 1;          // 全局变量
static int staticGlobalVar = 1; // 静态全局变量

void Test() {
    static int staticVar = 1; // 静态局部变量
    int localVar = 1;         // 局部变量
    int num1[10] = {1,2,3,4};// 局部数组
    char char2[] = "abcd";    // 局部数组(存储字符串拷贝)
    const char* pChar3 = "abcd";// 指针(指向字符串常量)
    int* ptr1 = (int*)malloc(sizeof(int)*4); // 动态内存指针
    int* ptr2 = (int*)calloc(4, sizeof(int)); // 动态内存指针
    int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4); // 扩容指针
    free(ptr1);
    free(ptr3);
}
cpp 复制代码
// 基于上面的Test函数
cout << sizeof(num1) << endl;    // 答案:40(10个int,每个4字节)
cout << sizeof(char2) << endl;   // 答案:5(含字符串结束符'\0')
cout << strlen(char2) << endl;   // 答案:4(仅统计有效字符,不含'\0')
cout << sizeof(pChar3) << endl;  // 答案:8(64位平台指针大小,32位为4)
cout << strlen(pChar3) << endl;  // 答案:4(字符串常量"abcd"长度)
cout << sizeof(ptr1) << endl;    // 答案:8(64位平台指针大小)

1.2 C 语言动态内存管理(malloc/calloc/realloc/free)

1.2.1 函数用法与核心区别

cpp 复制代码
#include <stdlib.h>
#include <stdio.h>

void Test() {
    // 1. malloc:申请空间,不初始化
    int* p1 = (int*)malloc(sizeof(int)*4); // 申请16字节,内容随机
    free(p1);
    p1 = NULL; // 避免野指针

    // 2. calloc:申请空间并初始化为0
    int* p2 = (int*)calloc(4, sizeof(int)); // 申请16字节,每个字节为0
    // 3. realloc:扩容/缩容已申请的空间
    // 扩容后:若原空间后有足够空间,直接扩展;否则重新分配新空间,拷贝原数据并释放旧空间
    int* p3 = (int*)realloc(p2, sizeof(int)*8); // 扩容到32字节
    free(p3);
    p3 = NULL;
}

三大函数核心区别:

|---------|----------------|---------------------|
| malloc | 不初始化(随机值) | 单个参数(总字节数) |
| calloc | 初始化为 0 | 两个参数(元素个数、单个元素 字节数) |
| realloc | 保留原数据(扩容 / 缩容) | 两个参数(原指针、新总字节数) |

1.2.2 常见问题:realloc 后是否需要 free 原指针(p2)?

答:不需要!原因:

  • 若 realloc 扩容成功:
    • 情况 1:原空间后有足够空间,直接扩展,返回原指针(p2 和 p3 指向同一地址),free (p3) 即释放原空间;
    • 情况 2:原空间后无空间,重新分配新空间,拷贝原数据并自动释放旧空间,p2 变为野指针,只需 free (p3)。
  • 若 realloc 失败:返回 NULL,原空间(p2)未释放,需手动 free (p2)。

1.3. C++ 动态内存管理(new/delete)

1.3.1 操作内置类型(申请 + 初始化 + 释放)

cpp 复制代码
#include <iostream>
using namespace std;

void Test() {
    // 1. 申请单个int空间(不初始化)
    int* p1 = new int;
    // 2. 申请单个int空间并初始化为10
    int* p2 = new int(10);
    // 3. 申请10个int空间(不初始化,C++11支持初始化:new int[10]{1,2,3})
    int* p3 = new int[10];

    // 释放空间(必须匹配使用)
    delete p1;     // 释放单个元素
    delete p2;     // 释放单个元素(带初始化)
    delete[] p3;   // 释放连续空间(数组)

    p1 = p2 = p3 = NULL; // 避免野指针
}

int main() {
    Test();
    return 0;
}

1.3.2 操作自定义类型(自动调用构造 / 析构函数)

cpp 复制代码
#include <iostream>
using namespace std;

class A {
public:
    A(int a = 0) : _a(a) {
        cout << "A():" << this << endl; // 构造函数
    }
    ~A() {
        cout << "~A():" << this << endl; // 析构函数
    }
private:
    int _a;
};

int main() {
    // C语言malloc:仅开辟空间,不调用构造函数
    A* p1 = (A*)malloc(sizeof(A));
    free(p1); // 仅释放空间,不调用析构函数
    p1 = NULL;

    // C++ new:开辟空间 + 调用构造函数
    A* p2 = new A(10);
    delete p2; // 调用析构函数 + 释放空间
    p2 = NULL;

    // 数组:new[]调用N次构造,delete[]调用N次析构
    A* p3 = new A[3]; // 3次构造
    delete[] p3;      // 3次析构
    p3 = NULL;

    return 0;
}
cpp 复制代码
A():(0x7ffee3b558a0)
~A():(0x7ffee3b558a0)
A():(0x7ffee3b55890)
A():(0x7ffee3b55888)
A():(0x7ffee3b55880)
~A():(0x7ffee3b55880)
~A():(0x7ffee3b55888)
~A():(0x7ffee3b55890)

1.3.3 核心注意点:new [] 与 delete [] 必须匹配

  • 若用new[]申请数组,必须用delete[]释放:否则自定义类型会漏调用部分析构函数,导致资源泄漏;
  • 内置类型匹配错误可能不报错,但不推荐(行为未定义);
  • 错误示例:A* p = new A[3]; delete p;(仅调用 1 次析构,泄漏 2 个对象资源)。

1.4 operator new 与 operator delete 函数

1.4.1 核心本质:全局函数(new/delete 的底层依赖)

new 和 delete 是操作符 ,底层实际调用全局函数operator newoperator delete申请 / 释放空间。

1.4.2 底层实现与失败处理

cpp 复制代码
// operator new:底层调用malloc,失败抛异常(而非返回NULL)
void* operator new(size_t size) {
    void* p = malloc(size);
    while (p == NULL) {
        // 尝试执行用户自定义的空间不足应对措施
        if (_callnewh(size) == 0) {
            throw std::bad_alloc(); // 抛异常
        }
        p = malloc(size);
    }
    return p;
}

// operator delete:底层调用free
void operator delete(void* p) {
    if (p != NULL) {
        free(p);
    }
}

1.4.3 关键注意点

  • operator new/operator delete可被用户重载(类内或全局),默认全局版本调用 malloc/free;
  • 与 malloc 的区别:malloc 失败返回 NULL,operator new失败抛bad_alloc异常;
  • operator new[]/operator delete[]:数组版本,底层调用operator new/operator delete,额外处理数组长度信息。

1.5 new 和 delete 的实现原理

1.5.1 内置类型

  • new ≈ operator new + 空间分配(无初始化,除非显式指定);
  • delete ≈ operator delete + 空间释放;
  • new [] ≈ operator new [] + 分配 N 个元素空间;
  • delete [] ≈ operator delete [] + 释放 N 个元素空间;
  • 核心区别:new 失败抛异常,malloc 返回 NULL。

1.5.2 自定义类型

new 的原理(3 步)
  1. 调用operator new函数申请空间;
  2. 在申请的空间上自动调用构造函数,完成对象初始化;
  3. 返回对象地址。
delete 的原理(3 步)
  1. 调用对象的析构函数,完成资源清理;
  2. 调用operator delete函数释放对象空间;
  3. 指针置空(用户手动操作)。
new [] 的原理(4 步)
  1. 调用operator new[]函数,底层调用operator new申请 N 个对象的总空间(含数组长度信息);
  2. 在空间上调用 N 次构造函数,初始化每个对象;
  3. 返回数组首地址。
delete [] 的原理(4 步)
  1. 从数组首地址获取数组长度 N;
  2. 调用N 次析构函数,清理每个对象资源;
  3. 调用operator delete[]函数,底层调用operator delete释放空间;
  4. 指针置空(用户手动操作)。

1.6 定位 new 表达式(placement-new)

1.6.1 通俗概念

已分配的原始内存中显式调用构造函数初始化对象(内存池场景常用,内存池分配的内存未初始化)。

1.6.2 用法格式与代码示例

cpp 复制代码
#include <iostream>
using namespace std;

class A {
public:
    A(int a = 0) : _a(a) {
        cout << "A():" << this << endl;
    }
    ~A() {
        cout << "~A():" << this << endl;
    }
private:
    int _a;
};

int main() {
    // 1. 用malloc申请原始内存(未调用构造函数,不是完整对象)
    A* p1 = (A*)malloc(sizeof(A));
    // 定位new:在p1指向的内存中调用构造函数(无参数)
    new(p1)A; 
    // 定位new:带参数初始化
    // new(p1)A(10);

    // 2. 用operator new申请原始内存
    A* p2 = (A*)operator new(sizeof(A));
    new(p2)A(20); // 带参数初始化

    // 3. 手动调用析构函数(必须!定位new不会自动调用)
    p1->~A();
    p2->~A();

    // 4. 释放原始内存
    free(p1);
    operator delete(p2);

    p1 = p2 = NULL;
    return 0;
}
cpp 复制代码
A():(0x7ffee3b558a0)
A():(0x7ffee3b55890)
~A():(0x7ffee3b558a0)
~A():(0x7ffee3b55890)

适用场景

  • 内存池:内存池分配的内存是原始内存(无构造),需用定位 new 初始化自定义类型对象;
  • 避免频繁申请释放内存:提前分配大块内存,用定位 new 重复初始化对象。

1.7 高频面试题(内存管理核心)

1.7.1 malloc/free 与 new/delete 的 7 大区别(面试必问)

|-------|--------------------------|--------------------------|
| 特性 | malloc/free | new/delete |
| 本质 | 库函数 | 操作符 |
| 初始化 | 不初始化(随机值) | 可初始化(如new int(10)) |
| 空间计算 | 需手动计算字节数(sizeof(int)*4) | 自动计算(new int[4]) |
| 返回值 | void*,需强转 | 对应类型指针,无需强转 |
| 失败处理 | 返回 NULL,需手动判空 | 抛bad_alloc异常,需捕获 |
| 自定义类型 | 仅开辟 / 释放空间,不调用构造 / 析构 | 开辟空间 + 构造,析构 + 释放空间 |
| 数组支持 | 需手动计算总字节数 | 直接用new[]/delete[],简洁 |

1.7.2 内存泄漏

(1)定义

程序未能释放不再使用的内存,导致内存浪费(内存未消失,而是失去控制)。

(2)危害
  • 短期运行程序:影响小;
  • 长期运行程序(操作系统、后台服务):内存逐渐耗尽,响应变慢,最终卡死。
(3)分类(核心关注 2 类)
  1. 堆内存泄漏(最常见):malloc/calloc/realloc/new 申请的内存未用 free/delete 释放;
  2. 系统资源泄漏:未释放系统资源(套接字、文件描述符、管道等)。
(4)检测方法
  • VS 环境:使用_CrtDumpMemoryLeaks()函数(简单检测,仅报泄漏字节数);
cpp 复制代码
#include <crtdbg.h>
int main() {
    int* p = new int[10];
    _CrtDumpMemoryLeaks(); // 程序退出时检测内存泄漏
    return 0;
}
  • Linux 环境:valgrind 工具(valgrind --leak-check=full ./a.out);
  • Windows 第三方工具:VLD(Visual Leak Detector,精准定位泄漏位置)。
(5)避免方法(事前预防为主)
  1. 养成良好编码习惯:申请内存后及时释放,匹配使用 malloc/free、new/delete;
  2. 采用 RAII 思想:用智能指针(shared_ptr/unique_ptr)管理动态内存;
  3. 使用内存管理库:公司内部私有库,自带泄漏检测;
  4. 借助工具:工程较大时用第三方工具检测。

二、模板初阶(泛型编程)

2.1 泛型编程(模板的核心目标)

2.1.1 问题引入:函数重载的 2 大缺点

cpp 复制代码
// 函数重载实现不同类型的Swap,缺点明显
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
void Swap(double& left, double& right) { double temp = left; left = right; right = temp; }
void Swap(char& left, char& right) { char temp = left; left = right; right = temp; }
  • 缺点 1:代码复用率低,新增类型需手动添加重载函数;
  • 缺点 2:可维护性差,一个函数出错,所有重载版本均受影响。

2.1.2 泛型编程定义

编写与类型无关的通用代码,通过模板让编译器根据不同类型生成具体代码,是代码复用的核心手段。


2.2 函数模板

2.2.1 概念与格式

  • 概念:代表一个函数家族,与类型无关,使用时参数化,生成具体类型函数;
  • 格式:template<typename T1, typename T2,...> + 函数定义;
  • 关键字:typename可替换为class不能用 struct)。
cpp 复制代码
#include <iostream>
using namespace std;

// 函数模板:通用Swap
template<typename T> // T为模板参数(类型占位符)
void Swap(T& left, T& right) {
    T temp = left;
    left = right;
    right = temp;
}

int main() {
    int a = 10, b = 20;
    Swap(a, b); // 编译器推演T为int,生成int版本Swap
    cout << "a=" << a << ", b=" << b << endl; // 输出:a=20, b=10

    double c = 1.1, d = 2.2;
    Swap(c, d); // 编译器推演T为double,生成double版本Swap
    cout << "c=" << c << ", d=" << d << endl; // 输出:c=2.2, d=1.1

    char e = 'A', f = 'B';
    Swap(e, f); // 编译器推演T为char,生成char版本Swap
    cout << "e=" << e << ", f=" << f << endl; // 输出:e=B, f=A

    return 0;
}

2.2.2 实现原理

函数模板本身不是函数,是编译器生成具体类型函数的 "模具":

  • 编译阶段:编译器根据实参类型推演模板参数 T,生成对应类型的函数;
  • 例如:Swap(a,b)(int 类型)→ 生成void Swap(int&, int&)Swap(c,d)(double 类型)→ 生成void Swap(double&, double&)

2.2.3 实例化(生成具体类型函数)

实例化分为隐式实例化显式实例化

(1)隐式实例化:编译器根据实参自动推演 T
cpp 复制代码
template<class T>
T Add(const T& left, const T& right) {
    return left + right;
}

int main() {
    int a1 = 10, a2 = 20;
    Add(a1, a2); // 隐式推演T为int

    double d1 = 10.0, d2 = 20.0;
    Add(d1, d2); // 隐式推演T为double

    // 错误:编译器无法推演T(a1是int,d1是double,T需统一)
    // Add(a1, d1);

    // 解决方式1:强制类型转换
    Add(a1, (int)d1); // T推演为int
    // 解决方式2:显式实例化
    Add<double>(a1, d1); // 强制T为double

    return 0;
}
(2)显式实例化:手动指定 T 的类型
cpp 复制代码
int main() {
    int a = 10;
    double b = 20.0;
    // 显式实例化:<>中指定T为int,编译器尝试隐式类型转换
    Add<int>(a, b); 
    // 显式实例化:<>中指定T为double
    Add<double>(a, b);
    return 0;
}

2.2.4 模板参数匹配原则(3 大规则)

规则 1:非模板函数与同名函数模板可共存
cpp 复制代码
// 非模板函数(专门处理int)
int Add(int left, int right) {
    cout << "非模板函数 Add(int, int)" << endl;
    return left + right;
}

// 函数模板(通用版本)
template<class T>
T Add(T left, T right) {
    cout << "模板函数 Add(T, T)" << endl;
    return left + right;
}

void Test() {
    Add(1, 2); // 优先调用非模板函数(完全匹配)
    Add<int>(1, 2); // 显式实例化,调用模板生成的int版本
}
cpp 复制代码
非模板函数 Add(int, int)
模板函数 Add(T, T)
规则 2:优先调用非模板函数,模板可生成更匹配版本时选模板
cpp 复制代码
// 非模板函数(int类型)
int Add(int left, int right) {
    cout << "非模板函数 Add(int, int)" << endl;
    return left + right;
}

// 函数模板(支持不同类型参数)
template<class T1, class T2>
T1 Add(T1 left, T2 right) {
    cout << "模板函数 Add(T1, T2)" << endl;
    return left + right;
}

void Test() {
    Add(1, 2); // 非模板函数完全匹配,调用非模板
    Add(1, 2.0); // 模板生成更匹配版本(T1=int, T2=double),调用模板
}
cpp 复制代码
非模板函数 Add(int, int)
模板函数 Add(T1, T2)
规则 3:模板函数不支持自动类型转换,普通函数支持
cpp 复制代码
void Test() {
    Add(1, 2.0); // 模板函数:T1=int, T2=double(显式指定或推演,不自动转换)
    Add(1, (int)2.0); // 普通函数:自动转换为int,调用非模板
}

2.3 类模板

2.3.1 定义格式(以动态顺序表 Vector 为例)

cpp 复制代码
#include <iostream>
#include <cassert>
using namespace std;

// 类模板:Vector不是具体类,是生成具体类的模具
template<class T>
class Vector {
public:
    // 构造函数(初始化列表)
    Vector(size_t capacity = 10)
        : _pData(new T[capacity])
        , _size(0)
        , _capacity(capacity)
    {}

    // 析构函数:类内声明,类外定义
    ~Vector();

    // 成员函数
    void PushBack(const T& data) {
        // 扩容逻辑(简化版)
        if (_size >= _capacity) {
            T* temp = new T[_capacity * 2];
            for (size_t i = 0; i < _size; ++i) {
                temp[i] = _pData[i];
            }
            delete[] _pData;
            _pData = temp;
            _capacity *= 2;
        }
        _pData[_size++] = data;
    }

    void PopBack() {
        if (_size > 0) {
            --_size;
        }
    }

    size_t Size() const { return _size; }

    // 运算符重载:[]访问元素
    T& operator[](size_t pos) {
        assert(pos < _size);
        return _pData[pos];
    }

private:
    T* _pData;     // 动态数组
    size_t _size;  // 有效元素个数
    size_t _capacity; // 容量
};

// 类模板的类外成员函数定义:必须加模板参数列表
template<class T>
Vector<T>::~Vector() {
    if (_pData) {
        delete[] _pData;
        _pData = NULL;
        _size = _capacity = 0;
    }
}

2.3.2 类模板的实例化(必须显式指定类型)

类模板实例化与函数模板不同,必须显式指定模板参数,实例化后的结果才是具体类。

cpp 复制代码
int main() {
    // Vector<int>是具体类,s1是该类的对象
    Vector<int> s1;
    s1.PushBack(1);
    s1.PushBack(2);
    s1.PushBack(3);
    cout << "s1.Size()=" << s1.Size() << endl; // 输出:3
    cout << "s1[1]=" << s1[1] << endl; // 输出:2

    // Vector<double>是另一个具体类,与Vector<int>完全独立
    Vector<double> s2;
    s2.PushBack(1.1);
    s2.PushBack(2.2);
    cout << "s2[0]=" << s2[0] << endl; // 输出:1.1

    return 0;
}

2.3.3 关键注意点

  • 类模板名(如 Vector)不是具体类,实例化后的Vector<int>Vector<double>才是具体类;
  • 不同实例化类型是完全独立的类,占用不同内存空间;
  • 类外定义成员函数时,必须加模板参数列表(template<class T>),且类名需带模板参数(Vector<T>::~Vector())。

三、学习总结与建议

内存管理部分

  1. 核心逻辑:掌握 "申请 - 初始化 - 使用 - 释放" 的完整流程,重点区分 malloc/new 的底层差异和适用场景;
  2. 重点突破:内存区域分布(真题必考)、new/delete 原理(自定义类型的构造 / 析构)、内存泄漏(面试高频);
  3. 避坑清单:new [] 与 delete [] 必须匹配、定位 new 需手动调用析构、new 失败抛异常需捕获。

模板部分

  1. 核心逻辑:模板是泛型编程的基础,本质是 "编译器生成代码的模具",解决代码复用问题;
  2. 重点突破:函数模板的实例化(隐式 + 显式)、匹配原则、类模板的类外成员函数定义与实例化;
  3. 避坑清单:模板参数关键字用 typename/class(不用 struct)、类模板必须显式实例化、模板函数不自动类型转换。

相关推荐
czy87874752 小时前
深入了解 C++ 中的 Lambda 表达式(匿名函数)
c++
qq_12498707532 小时前
基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)
java·开发语言·前端·spring boot·spring·毕业设计·计算机毕业设计
CSDN_RTKLIB2 小时前
include_directories和target_include_directories说明
c++
froginwe113 小时前
Python 条件语句
开发语言
七夜zippoe3 小时前
Python统计分析实战:从描述统计到假设检验的完整指南
开发语言·python·统计分析·置信区间·概率分布
2601_949146533 小时前
Python语音通知API示例代码汇总:基于Requests库的语音接口调用实战
开发语言·python
Trouvaille ~3 小时前
【Linux】UDP Socket编程实战(二):网络字典与回调设计
linux·运维·服务器·网络·c++·udp·操作系统
3GPP仿真实验室3 小时前
【Matlab源码】6G候选波形:OFDM-IM 索引调制仿真平台
开发语言·matlab
明洞日记3 小时前
【图解软考八股034】深入解析 UML:识别标准建模图示
c++·软件工程·软考·uml·面向对象·架构设计