掌握C++核心特性

目标: 掌握C++核心特性,为嵌入式开发打基础

好的,我来为你详细梳理一下 继承与多态、虚函数 相关的知识点,包括单继承、多继承、虚函数表机制、纯虚函数与抽象类、动态绑定。以下内容适合中等难度层次的理解,便于考试复习或面试准备。


🌟 继承与多态,虚函数


1️⃣ 单继承和多继承

单继承
  • 一个派生类只有一个基类。

  • 结构简单,层次清晰。

  • 示例:

    class Base {
    public:void show() { std::cout << "Base" << std::endl; }
    };class Derived : public Base {
    public:void display() { std::cout << "Derived" << std::endl; }
    };
    

public 公开继承(最常用,基类 public 和 protected 成员在派生类中保持原有权限)

protected 保护继承(基类 public 和 protected 成员在派生类中都变成 protected)

private 私有继承(基类 public 和 protected 成员在派生类中都变成 private)

多继承
  • 一个派生类可以同时继承多个基类。

  • 可带来灵活性,但也可能引发二义性问题(如菱形继承问题)。

  • 解决办法:虚继承(virtual 关键字)

  • 示例:

    class Base1 {
    public:void func1() { std::cout << "Base1" << std::endl; }
    };class Base2 {
    public:void func2() { std::cout << "Base2" << std::endl; }
    };class Derived : public Base1, public Base2 {
    };
    
菱形继承问题
class A { public: int x; };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };

👉 如果 BC 都虚继承自 AD 中只有一份 A 的成员。


🚀 构造函数后 : 的用途 —— 初始化列表

在 C++ 中,构造函数定义时可以用 : 引出一个 初始化列表,用于初始化成员变量和基类。

语法

class 类名 {
public:类型1 成员1;类型2 成员2;类名(参数列表) : 成员1(1), 成员2(2) {// 构造函数体}
};

🌟 初始化列表主要作用

✅ 初始化 const 成员(必须用初始化列表)
✅ 初始化 引用成员(必须用初始化列表)
✅ 调用基类构造函数(在继承中必用)
✅ 效率更高(成员在进入构造函数体之前就已初始化)


🌰 示例 1:普通成员初始化

class Point {int x;int y;
public:Point(int a, int b) : x(a), y(b) {// 构造函数体可以为空}
};

等价于:

Point p(1, 2);

这里 x 初始化为 1,y 初始化为 2。


🌰 示例 2:const 和引用成员

class Example {const int ci;int& ref;
public:Example(int i, int& r) : ci(i), ref(r) { }
};

👉 注意:const& 成员必须在初始化列表里赋值,不能在构造函数体内赋值。


🌰 示例 3:继承情况下,调用基类构造函数

class Base {
public:Base(int a) { std::cout << "Base: " << a << std::endl; }
};class Derived : public Base {
public:Derived(int a, int b) : Base(a) {std::cout << "Derived: " << b << std::endl;}
};

当你写:

Derived d(10, 20);

输出:

Base: 10
Derived: 20

✅ 先调用基类构造函数。


用法符号 : 位置例子
类继承声明class A : public Bclass B : public A { };
初始化列表构造函数头部后A(int x) : a(x) { }

🌟 初始化列表主要作用

✅ 初始化 const 成员(必须用初始化列表)
✅ 初始化 引用成员(必须用初始化列表)
✅ 调用基类构造函数(在继承中必用)
✅ 效率更高(成员在进入构造函数体之前就已初始化)

普通 const 成员是属于对象的,每个对象的 const 成员值可能不同。
必须在构造对象时确定 const 成员值,所以需要初始化列表。
👉 引用必须在定义时绑定到对象(或变量)上,不能晚绑定。
一旦引用被初始化(绑定),它就永远指向这个对象或变量,不可改变。


特性const 成员变量static const 成员变量
初始化方式必须用初始化列表初始化可以在类内初始化
属于对象的每个实例类的所有实例共享一份
例子A(int v) : x(v) {}static const int x = 10;

2️⃣ 虚函数表机制

  • 虚函数表 (vtable):编译器为含有虚函数的类生成的一张函数地址表。
  • 虚指针 (vptr):每个含虚函数的对象实例中包含一个指向虚函数表的指针。
  • 派生类覆盖虚函数时,vtable 的相应入口会被派生类的函数地址替换。
  • 调用虚函数时,根据 vptr 定位 vtable,再调用正确的函数地址,实现多态。
class Base {
public:virtual void func() { std::cout << "Base::func" << std::endl; }
};class Derived : public Base {
public:void func() override { std::cout << "Derived::func" << std::endl; }
};

📌 当你写 Base* p = new Derived(); p->func(); 时,会通过 vptr 查找 vtable 中的 Derived::func 地址。


3️⃣ 纯虚函数和抽象类

纯虚函数
  • 语法:virtual void func() = 0;
  • 没有实现,需要派生类重写。
抽象类
  • 包含至少一个纯虚函数的类。
  • 无法实例化对象,只能作为基类。

示例:

class Shape {
public:virtual void draw() = 0; // 纯虚函数
};class Circle : public Shape {
public:void draw() override { std::cout << "Draw Circle" << std::endl; }
};

👉 Shape s; ❌不允许
👉 Shape* ps = new Circle(); ✅允许,用基类指针指向子类对象。


4️⃣ 动态绑定

  • 动态绑定(又称 运行时多态):在运行时确定调用哪个函数。

  • 前提:

    • 函数是虚函数。
    • 通过基类指针或引用调用。
  • 如果不满足上面条件,编译时静态绑定。

示例:

Base* p = new Derived();
p->func(); // 动态绑定,调用 Derived::func

如果是 p->Base::func(); 则会静态绑定,强制调用基类版本。


💡 总结

特性单继承多继承虚函数纯虚函数动态绑定
关系一个基类多个基类实现多态实现接口运行时确定函数
优点简单易维护灵活多态行为强制派生类实现多态支持
缺点功能受限易引发二义性增加内存开销不能实例化基类性能略低于静态绑定

继承与 vptr 重新赋值的背景

  • 每个对象的 vptr 用来指向当前对象所属类的虚函数表(vtable)。
  • 当你创建一个派生类对象时,这个对象其实包含了基类子对象部分。
  • 在构造过程中,随着构造函数的调用,vptr 会被设置为对应类的 vtable

两个对象:
各自有一个 vptr
vptr 都指向同一张 vtable(Derived 的 vtable)

vtable 是编译器为类生成的唯一一张表(每个带虚函数的类一张)


🌟 C++ 模板基础


1️⃣ 函数模板语法

👉 语法:

template <typename T>
T add(T a, T b) {return a + b;
}

或:

template <class T>  // typename 和 class 都可以
T add(T a, T b) {return a + b;
}

👉 使用:

add(3, 4);      // T 推导为 int
add(3.5, 4.2);  // T 推导为 double
add<int>(3, 4); // 显式指定 T = int

2️⃣ 类模板实现

👉 语法:

template <typename T>
class MyClass {
public:T data;MyClass(T val) : data(val) {}void show() { std::cout << data << "\n"; }
};

👉 使用:

MyClass<int> obj1(10);
MyClass<std::string> obj2("hello");

👉 注意:

  • 必须在使用类模板时提供模板参数(除非有默认参数)。
  • 类模板成员函数定义可以在类外,但必须带模板头:
template <typename T>
void MyClass<T>::show() {std::cout << data << "\n";
}

3️⃣ 模板特化

👉 全特化:

template <typename T>
class Printer {
public:void print(T val) { std::cout << val << "\n"; }
};// 对 char* 的特化
template <>
class Printer<char*> {
public:void print(char* val) { std::cout << "char* : " << val << "\n"; }
};

👉 偏特化:

template <typename T, typename U>
class Pair { /* ... */ };template <typename T>
class Pair<T, int> { /* 针对第二个参数是 int 的特化 */ };

4️⃣ 模板参数推导

👉 函数模板支持参数推导:

template <typename T>
void func(T val) { /* ... */ }func(10);         // 推导 T=int
func(3.14);       // 推导 T=double
func("hello");    // 推导 T=const char*

👉 类模板 不支持自动推导(C++17 前),但 C++17 起支持 类模板参数推导引擎

template <typename T>
class Wrapper {
public:T value;Wrapper(T v) : value(v) {}
};Wrapper w(10);  // C++17 起推导出 Wrapper<int>

🌟 小结表

模板特性作用特点
函数模板让函数支持不同类型支持参数推导,可显式指定
类模板让类支持不同类型使用时需指定参数(除非 C++17 推导)
模板特化针对特定类型提供不同实现全特化或偏特化
参数推导自动根据实参确定模板参数类模板一般不推导,函数模板支持

🌟 C++ 异常处理基础


1️⃣ 异常处理机制

基本语法:
try {// 可能抛出异常的代码throw std::runtime_error("Error occurred");
}
catch (const std::runtime_error& e) {std::cout << "Caught: " << e.what() << "\n";
}
catch (...) {std::cout << "Caught unknown exception\n";
}

👉 流程

  • try 块中代码运行时遇到 throw,立即停止执行,跳转到对应 catch
  • 匹配的 catch 语句被调用。
  • 如果没有匹配的 catch,程序调用 std::terminate()

👉 注意

  • throw; 可以重新抛出当前捕获的异常。
  • 异常匹配是按照 catch 的顺序自上而下。

2️⃣ 自定义异常类

你可以自定义异常类型,通常继承自 std::exception 或其子类。

class MyException : public std::exception {
public:const char* what() const noexcept override {return "My custom exception";}
};

使用:

try {throw MyException();
}
catch (const MyException& e) {std::cout << e.what() << "\n";
}

3️⃣ RAII 与异常安全

RAII(资源获取即初始化)是 C++ 保证异常安全的重要手段。

例子:
class FileWrapper {FILE* fp;
public:FileWrapper(const char* filename) {fp = fopen(filename, "r");if (!fp) throw std::runtime_error("File open failed");}~FileWrapper() {if (fp) fclose(fp);}
};

✔ 如果在构造中抛出异常,已构造对象的析构函数会被自动调用,资源得到释放。
✔ 这就是 异常安全 的 RAII 精神。


4️⃣ 嵌入式系统中异常使用注意事项

👉 为什么嵌入式常禁用异常?

  • 异常会增加代码体积(嵌入式对ROM/Flash大小敏感)
  • 异常处理可能需要栈展开,增加运行时开销
  • 嵌入式通常要求可控、确定性的错误处理

👉 替代方案

  • 返回错误码
  • 使用断言 assert
  • 用状态机或专用错误处理函数

👉 嵌入式项目编译器一般禁用异常

g++ -fno-exceptions ...

🌟 小结表

特性描述注意事项
try-catch-throw异常处理结构,用于捕获和处理异常匹配顺序重要,throw 可重新抛出
自定义异常类提供更清晰的异常类型继承自 std::exception,重写 what()
RAII自动管理资源,防止泄漏析构函数释放资源,保证异常安全
嵌入式异常通常禁用,因开销大推荐用错误码或断言代替

🌟 C++ 异常为什么会增大代码空间?

因为 编译器为了支持异常处理,需要生成额外的元数据、表和隐藏代码。主要包括以下几个方面:


1️⃣ 栈展开(stack unwinding)信息

👉 当你 throw 异常时,程序必须从抛出点开始,依次调用每个对象的析构函数,正确释放资源。

💡 为了做到这点,编译器会:

  • 在二进制中生成一个“异常处理表”(也叫 栈展开表unwind table
  • 记录每个函数的栈帧布局、哪些地方有局部对象、析构函数地址等

⚠ 这些表存在于可执行文件中(通常是 .eh_frame 段),直接占用代码空间。


2️⃣ 异常处理控制逻辑

👉 编译器生成隐藏代码:

  • 检测抛出异常的地方
  • 跳转到异常处理器(catch 代码)
  • 调用析构函数做清理

这些代码虽然不显式写在源代码中,但会体现在最终的机器码中。


3️⃣ 多余的辅助代码 / 运行库支持

👉 异常处理需要运行库提供辅助函数,比如:

  • 异常对象的创建、复制、销毁逻辑
  • 抛出异常时调用的全局函数(例如 __cxa_throw__cxa_begin_catch 等,GCC/Clang 下)

👉 这部分库代码也会被链接进你的程序中,增加体积。


🌈 对比:C vs C++ 错误处理

特性C语言C++异常
机制返回值、errnosetjmp/longjmptry-catch-throw
自动清理❌ 无,必须手工清理✅ 自动调用析构,RAII
错误传播必须层层传递或显式 longjmp自动沿调用栈寻找 catch
可读性易出错、代码繁琐更简洁、可读性好
性能开销小,简单高效异常表增加代码体积,栈展开有开销

🌟 你问得很好!咱们一起把“C语言需要根据错误手动操作、层层处理”这个概念彻底搞清楚。


🔑 为什么说 C 需要“层层”处理错误?

在 C 语言里,函数出错后,它本身不会“跳回去”或者自动通知调用者发生了什么(不像 C++ 异常可以自动向上传递)。
👉 你得 显式返回错误码,并在每一层调用代码里手工检查。


🌰 例子:层层返回错误码

假设你写一个程序,调用很多函数,某一层出错了,需要上层知道:

#include <stdio.h>int lowLevel(int a) {if (a == 0) {return -1;  // 出错:除 0}return 100 / a;
}int midLevel(int a) {int res = lowLevel(a);if (res == -1) {return -1;  // 出错,继续向上报告}return res + 10;
}int highLevel(int a) {int res = midLevel(a);if (res == -1) {return -1;  // 出错,继续向上报告}return res * 2;
}int main() {int result = highLevel(0);if (result == -1) {printf("Error happened!\n");} else {printf("Result = %d\n", result);}
}

✅ 这就是所谓 层层检查、层层返回
每一层都必须写:

if (返回值 == 错误码) return 错误码;

否则,错误就会“漏掉”。


🔥 和 C++ 异常对比:自动向上传递

同样逻辑如果是 C++:

int lowLevel(int a) {if (a == 0) throw std::runtime_error("div by zero");return 100 / a;
}int midLevel(int a) {return lowLevel(a) + 10;
}int highLevel(int a) {return midLevel(a) * 2;
}int main() {try {int result = highLevel(0);std::cout << "Result = " << result << "\n";} catch (const std::exception& e) {std::cout << "Error: " << e.what() << "\n";}
}

✅ 你不用每层写“检查返回值、return -1”了,异常会自动穿过每一层,直到 catch


🌈 层层检查的含义总结
C 语言层层检查C++ 异常
每层手动检查返回值或 errno异常自动向上传递到最近的 catch
错误处理代码和主逻辑混在一起,容易混乱错误处理代码集中在 catch,主逻辑更清晰
易写漏检查,容易埋 bug不易漏掉错误(除非没有 catch

C 的手工层层处理的风险

  • 容易漏掉某层的检查,错误被默默吞掉
  • 错误码设计混乱时,调试困难
  • 错误处理代码重复多,维护麻烦

🚀 STL 容器基础 (vector, array, list)


1️⃣ 容器的选择原则

vector

  • 连续内存块(像动态数组)
  • 随机访问快(支持 []at()
  • 尾部插入/删除快(push_back / pop_back O(1))
  • 中间/开头插入删除慢(需移动元素)

array

  • 固定大小、栈分配(本质是封装了 C 风格数组)
  • 编译期大小确定
  • 非常轻量、效率高(零开销封装)

list

  • 双向链表
  • 任意位置插入/删除 O(1)
  • 不支持随机访问(无 []

💡 选择思路:

需求容器
需要频繁随机访问vectorarray
需要频繁插入删除(中间或两端)list
大小固定、性能极高array
动态大小、插尾快vector

2️⃣ 迭代器使用

所有 STL 容器都支持迭代器,用于统一遍历:

#include <vector>
#include <iostream>int main() {std::vector<int> v = {1, 2, 3, 4};// 普通迭代器for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {std::cout << *it << " ";}std::cout << "\n";// 范围 for(C++11)for (auto x : v) {std::cout << x << " ";}std::cout << "\n";// 反向迭代器for (auto it = v.rbegin(); it != v.rend(); ++it) {std::cout << *it << " ";}std::cout << "\n";
}

👉 注意:

  • vector:随机访问迭代器
  • list:双向迭代器(不能做随机访问操作)

3️⃣ 算法库基础

STL 提供强大算法,与容器无关:

#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> v = {4, 1, 3, 2};std::sort(v.begin(), v.end());  // 排序std::for_each(v.begin(), v.end(), [](int x) {std::cout << x << " ";});std::cout << "\n";auto it = std::find(v.begin(), v.end(), 3);  // 查找if (it != v.end()) {std::cout << "Found: " << *it << "\n";}
}

💡 STL 算法特点:

  • 和容器解耦(基于迭代器工作)
  • 提供排序、查找、修改、统计等功能
  • 支持自定义谓词(lambda、函数对象)

4️⃣ 嵌入式环境下 STL 使用注意事项

嵌入式开发中 STL 使用会遇到一些问题:

动态分配

  • vectorlist 都依赖堆内存,嵌入式堆内存可能紧张或管理严格。

代码膨胀

  • 模板、泛型带来代码体积增大。

实时性

  • 有些操作(如 vector 扩容)可能会带来不可预测的耗时。

对策

  • 优先选用 array(栈上固定大小,零开销封装)
  • 或自定义 allocator 控制内存管理
  • 或使用轻量级替代库(如:Embedded STL、ETL)

🌈 小结

容器优势适用场景
vector动态大小、随机访问快数组替代、需要动态增长
array固定大小、栈分配、零开销大小固定、嵌入式友好
list插入删除快、稳定迭代器频繁中间操作、元素数量不大
嵌入式建议
尽量避免堆分配容器(vector、list)
array 或静态分配的容器
控制代码体积(注意模板实例化膨胀)

迭代器类自己重载了运算符

这些迭代器类型都会重载必要的运算符,比如:

  • *it —— 重载了 operator*(),返回元素引用
  • ++it —— 重载了 operator++(),让迭代器移动到下一个元素
  • --it —— 重载了 operator--()(如果支持)
  • it + nit - n —— 只有随机访问迭代器(如 vector)会重载
  • it == it2it != it2 —— 重载了比较运算符

不同容器的迭代器能力不同

容器迭代器类型支持运算符
vector / array随机访问迭代器++, --, +, -, []
list双向迭代器++, --
forward_list单向迭代器只能 ++

🚀 智能指针 (unique_ptr, shared_ptr)

智能指针(std::unique_ptr, std::shared_ptr, std::weak_ptr)
→ 属于 C++ 标准库的一部分
→ 定义在 头文件中
→ 实现基于模板、RAII、引用计数等技术,但不归类在 STL 容器/算法里

1️⃣ RAII 原理

RAII 全称:
👉 Resource Acquisition Is Initialization
👉 资源获取即初始化

🚀 RAII 的核心思想

把资源(内存、文件句柄、锁、网络连接等)的管理交给对象的生命周期。

也就是说:

  • 对象创建时(构造函数):获取资源
  • 对象销毁时(析构函数):释放资源

💡 用对象的生命周期保证资源安全,不需要手工释放。

🌈 RAII 的应用场景

  • 智能指针(unique_ptr, shared_ptr)管理内存
  • fstream 管理文件句柄
  • lock_guard / unique_lock 管理锁
  • 各种容器管理内部数据
  • 自定义资源类(比如管理数据库连接、网络 socket)

🌰 RAII 例子

裸指针(非 RAII)

void foo() {int* p = new int(10);// ... 使用 pdelete p;  // 别忘了!否则内存泄漏
}

❌ 如果程序中途抛异常或 return,可能忘了 delete。

💡 智能指针就是 RAII 的经典应用:

  • 构造时接管裸指针(new 的结果)
  • 析构时自动 delete,防止内存泄漏
{std::unique_ptr<int> p(new int(10)); // 自动管理内存,无需手动 delete
} // 作用域结束,自动 delete


RAII 的实现逻辑

RAII 的实现 = 用类封装资源

  • 构造函数:负责获取资源
  • 析构函数:负责释放资源

资源对象的生命周期 = 资源的生命周期
对象被创建时 → 自动获得资源
对象被销毁时 → 自动释放资源


🌰 手写一个简单 RAII 类(以文件操作为例)

我们不用 std::ofstream,自己实现一个简单 RAII 文件类:

#include <iostream>
#include <cstdio>class FileRAII {
private:FILE* file;public:// 构造函数:打开文件FileRAII(const char* filename, const char* mode) {file = std::fopen(filename, mode);if (!file) {throw std::runtime_error("Failed to open file");}std::cout << "File opened\n";}// 提供操作文件的方法void write(const char* text) {if (file) {std::fputs(text, file);}}// 析构函数:关闭文件~FileRAII() {if (file) {std::fclose(file);std::cout << "File closed\n";}}// 禁止拷贝,防止重复关闭FileRAII(const FileRAII&) = delete;FileRAII& operator=(const FileRAII&) = delete;
};

🌟 使用例子

int main() {try {FileRAII file("test.txt", "w");file.write("Hello RAII!");// 不需要手工 fclose,离开作用域自动关闭} catch (const std::exception& e) {std::cerr << e.what() << "\n";}return 0;
}

🌈 RAII 实现的关键点
部分作用
构造函数获取资源(例如分配内存、打开文件、加锁)
析构函数释放资源(例如释放内存、关闭文件、解锁)
禁止拷贝防止多个对象共享同一资源导致重复释放
可选:支持移动语义允许资源转移所有权(C++11 以后推荐)

RAII 本质就是编写一个资源封装类,通过构造 + 析构管理资源,让对象生命周期决定资源管理,无需手工操作。


🚀 RAII + 异常处理:优雅管理资源

在 C++ 中,经常这样用:

try {SomeRAIIObject obj;// ... 可能抛异常的代码 ...
} catch (...) {// 处理异常
}
// 无论如何 obj 析构、资源释放

💡 这就是 现代 C++ 推荐风格:RAII 负责资源安全,异常处理负责逻辑控制。

2️⃣ unique_ptr 使用场景

特点

  • 独占所有权(禁止拷贝,只允许移动)
  • 不允许多个 unique_ptr 管同一块内存

使用场景

  • 确保资源唯一所有权
  • 避免手写 delete,防止泄漏
  • 用在工厂函数、返回局部对象时:
std::unique_ptr<Foo> createFoo() {return std::unique_ptr<Foo>(new Foo);
}
  • 用于指向大对象、避免复制

移动所有权

std::unique_ptr<int> p1(new int(10));
std::unique_ptr<int> p2 = std::move(p1);  // p1 放弃所有权

3️⃣ shared_ptr 引用计数

特点

  • 多个 shared_ptr 可以共享一块内存的所有权
  • 内部维护一个 引用计数
  • 最后一个 shared_ptr 被销毁时,资源才被释放

引用计数机制

auto p1 = std::make_shared<int>(10);
auto p2 = p1;  // 引用计数 +1
auto p3 = p2;  // 引用计数 +1
// p1, p2, p3 全部销毁后,delete 内存

你可以通过 use_count() 查看计数:

std::cout << p1.use_count();  // 输出当前计数

4️⃣ 避免循环引用

循环引用问题
如果两个对象都用 shared_ptr 指向对方,会导致引用计数永远不为 0,内存无法释放。

💡 示例

struct B;
struct A {std::shared_ptr<B> bptr;
};
struct B {std::shared_ptr<A> aptr;
};

💥 这里 A 和 B 相互持有 shared_ptr,会产生循环引用。

解决方案

weak_ptr 打破循环

struct B;
struct A {std::shared_ptr<B> bptr;
};
struct B {std::weak_ptr<A> aptr;  // 不增加引用计数
};
  • weak_ptr 不会增加引用计数,只是观测对象是否还活着。
  • lock() 可以临时获得一个 shared_ptr
if (auto sp = aptr.lock()) {// 安全访问
}

🌈 小结对比

智能指针特点适用场景
unique_ptr独占、禁止拷贝独占资源,防止泄漏
shared_ptr引用计数、共享资源多方共享、动态生命周期管理
weak_ptr弱引用、不增加计数避免循环引用、观察 shared_ptr

🌟 C++ 智能指针主要就是 unique_ptr, shared_ptr, weak_ptr 三种,每种针对不同的所有权管理需求,配合 RAII 自动管理内存,防止泄漏。



RAII 类本身不需要配合智能指针

👉 RAII 类 = 自己封装了资源管理(构造获取资源 + 析构释放资源)
👉 它的资源管理已经是安全的,不依赖智能指针。

🌰 比如:

{FileRAII file("test.txt", "w");file.write("Hello RAII");// 离开作用域时自动关闭文件,不需要智能指针
}

➡ 这里 RAII 类的对象本身就在栈上,出作用域自动销毁,资源释放。根本不需要智能指针参与。


🌈 什么时候会配合智能指针使用?

你可能会 动态创建 RAII 对象,这时:

  • 如果你用 new 创建 RAII 对象,为避免手工 delete,就用智能指针管理它。
  • 特别是当 RAII 对象需要跨作用域、多处共享时,智能指针(如 unique_ptr / shared_ptr)就很方便。

🌰 示例:

#include <memory>
auto filePtr = std::make_unique<FileRAII>("test.txt", "w");
filePtr->write("Hello RAII");
// unique_ptr 离开作用域自动释放 FileRAII 对象,FileRAII 析构释放资源

👉 这里智能指针管理 RAII 对象本身的生命周期,RAII 对象内部管理资源的生命周期。


智能指针和 RAII 类的关系总结

情况是否需要智能指针
RAII 类对象在栈上声明(局部变量)❌ 不需要智能指针,作用域退出时自动释放
RAII 类对象动态创建(new)✅ 推荐用智能指针管理(避免手工 delete)
RAII 类对象需要跨多个作用域共享✅ 用 shared_ptr
RAII 类对象转移所有权✅ 用 unique_ptr

📝 核心思路

RAII 解决资源管理,智能指针解决对象管理。它们可以单独用,也可以组合用,取决于对象的使用方式。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/87979.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/87979.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python的高校教师资源管理系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…

Java Collections工具类:高效集合操作

Collections工具类概述 Collections是Java提供的集合操作工具类&#xff0c;位于java.util包中&#xff0c;包含大量静态方法&#xff0c;用于对List、Set、Map等集合进行排序、查找、替换、同步化等操作。 常用方法及代码示例 排序操作 sort(List<T> list)&#xff1a…

vue指令总结

vue指令总结 一、总述 二、代码实现&#xff08;内含大量注释&#xff09; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>vue入门</title><!-- 使用Vue 3官方CDN --><script src"https://unpkg.c…

RUP——统一软件开发过程

RUP概述 RUP&#xff08;Rational Unified Process&#xff09;&#xff0c;统一软件开发过程&#xff0c;统一软件过程是一个面向对象且基于网络的程序开发方法论。 在RUP中采用“41”视图模型来描述软件系统的体系结构。“41”视图包括逻辑视图、实现视图、进程视图、部署视…

SpringBoot电脑商城项目--增加减少购物车商品数量

1. 持久层 1.1 规划sql语句 执行更新t_cart表记录的num值根据cid查询购物车的数据是否存在 select * from t_cart where cid#{cid} 1.2 接口和抽象方法 /*** 获取购物车中商品的数据总数* return 购物车中商品的数据总数*/Cart findByCid(Integer cid); 1.3 xml文件中sql映射…

零基础学习Redis(13) -- Java使用Redis命令

上期我们学习了如何使用Java连接到redis&#xff0c;这期我们来学习如何在java中使用redis中的一些命令 1. set/get 可以看到jedis类中提供了很多set方法 public static void test1(Jedis jedis) {jedis.flushAll();jedis.set("key1", "v1");jedis.set(&q…

解决OSS存储桶未创建导致的XML错误

前言 在Java开发中&#xff0c;集成对象存储服务&#xff08;OSS&#xff09;时&#xff0c;开发者常会遇到一个令人困惑的错误提示&#xff1a; “This XML file does not appear to have any style information associated with it. The document tree is shown below.” 此…

Spring 表达式语言(SpEL)深度解析:从基础到高级实战指南

目录 一、SpEL是什么&#xff1f;为什么需要它&#xff1f; 核心价值&#xff1a; 典型应用场景&#xff1a; 二、基础语法快速入门 1. 表达式解析基础 2. 字面量表示 3. 属性访问 三、SpEL核心特性详解 1. 集合操作 2. 方法调用 3. 运算符大全 4. 类型操作 四、Sp…

算法导论第二十四章 深度学习前沿:从序列建模到创造式AI

第二十四章 深度学习前沿&#xff1a;从序列建模到创造式AI 算法的进化正在重新定义人工智能的边界 深度学习作为机器学习领域最活跃的分支&#xff0c;正以惊人的速度推动着人工智能的发展。本章将深入探讨五大前沿方向&#xff0c;通过原理分析、代码实现和应用场景展示&…

抽象工厂设计模式

1.问题背景&#xff1a; 现在有两个产品(Product)分别是手机壳(PhoneCase)和耳机(EarPhone)&#xff0c;但是他们会来自于各个生产厂商&#xff0c;比如说Apple和Android等等 那么至少会有四个产品&#xff0c;分别是安卓手机壳&#xff0c;安卓耳机&#xff0c;苹果手机壳&a…

GESP 3级 C++ 知识点总结

根据GESP考试大纲 (2024年3月版)&#xff0c;帮大家总结一下GESP 3级 C语言的知识点&#xff1a; 核心目标&#xff1a; 掌握C程序的基本结构&#xff0c;理解并能运用基础的编程概念解决稍复杂的问题&#xff0c;重点是函数、一维数组和字符串处理。 主要知识点模块&#x…

腾讯云主动型云蜜罐技术解析:云原生威胁狩猎的革新实践(基于腾讯云开发者社区技术网页与行业实践)

摘要 腾讯云主动型云蜜罐&#xff08;Active Cloud Honeypot&#xff09;通过动态诱捕机制和云原生架构&#xff0c;在威胁检测效率、攻击链还原深度、防御联动实时性等维度实现突破。相较于传统蜜罐&#xff0c;其核心优势体现在&#xff1a; 部署效率&#xff1a;分钟级完成…

企业微信wecom/jssdk的使用(入门)

概述 记录一个企业微信jssdk的使用&#xff0c;因为要用到图片上传、扫描二维码等工具。项目是uniapp开发的h5项目&#xff0c;fastadmin&#xff08;thinkphp5&#xff09;后端 先看官方文档 https://developer.work.weixin.qq.com/document/path/90547#%E5%BA%94%E7%94%A8…

大零售生态下开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新研究

摘要&#xff1a;在流量红利消退、零售形态多元化的背景下&#xff0c;大零售生态成为商业发展的新趋势。本文聚焦开源链动21模式、AI智能名片与S2B2C商城小程序在零售领域的协同应用&#xff0c;探讨其如何打破传统零售边界&#xff0c;实现流量变现与用户资产化。研究表明&am…

Scrapy全流程(一)

创建一个scrapy项目:scrapy startproject mySpider 生成一个爬虫:scrapy genspider douban movie.douban.com 提取数据:完善spider&#xff0c;使用xpath等方法 保存数据:pipeline中保存数据 2 创建scrapy项目 下面以抓取豆瓣top250来学习scrapy的入门使用&#xff1a;豆瓣…

【Elasticsearch】TF-IDF 和 BM25相似性算法

在 Elasticsearch 中&#xff0c;TF-IDF 和 BM25 是两种常用的文本相似性评分算法&#xff0c;但它们的实现和应用场景有所不同。以下是对这两种算法的对比以及在 Elasticsearch 中的使用情况&#xff1a; TF-IDF - 定义与原理&#xff1a;TF-IDF 是一种经典的信息检索算法&am…

【QT】控件二(输入类控件、多元素控件、容器类控件与布局管理器)

文章目录 1.输入类控件1.1 LineEdit1.2 Text Edit1.3 Combo Box1.4 SpinBox1.5 Date Edit & Time Edit1.6 Dial1.7 Slider 2. 多元素控件2.1 List Widget2.2 Table Widget2.3 Tree Widget 3. 容器类控件3.1 Group Box3.2 Tab Widget 4. 布局管理器4.1 垂直布局4.2 水平布局…

【Docker基础】Docker镜像管理:docker pull详解

目录 1 Docker镜像基础概念 1.1 什么是Docker镜像&#xff1f; 1.2 镜像与容器的关系 1.3 镜像仓库(Registry) 2 docker pull命令详解 2.1 基本语法 2.2 参数解释 2.3 拉取镜像的基本流程 2.4 镜像分层结构解析 3 docker pull实战指南 3.1 基本使用示例 3.2 指定镜…

PixPin:一个强大且免费的截图贴图工具

PixPin 是一款国产免费的截图工具&#xff0c;支持屏幕截图、屏幕录制&#xff08;GIF&#xff09;、文字识别&#xff08;OCR&#xff09;以及贴图等功能。 高效截图 PixPin 支持自由选择或自动检测窗口&#xff0c;自定义截图区域&#xff0c;像素级精确捕捉&#xff0c;延时…

【测试报告】论坛系统

一、项目背景 1.1 测试目标及测试任务 测试目标旨在保障功能无漏洞、流程顺畅&#xff0c;实现多端显示交互一致&#xff0c;达成高并发场景下响应时间&#xff1c;2 秒等性能指标&#xff0c;抵御 SQL 注入等安全攻击&#xff0c;提升 UI 易用性与提示友好度&#xff1b; 背…