当前位置: 首页 > news >正文

C/C++ 面试智能指针

说下你对智能指针的理解

回答1:

因为C++使用内存的时候很容易出现野指针、悬空指针、内存泄露的问题。所以C++11引入了智能指针来管理内存。有四种:

  • auto_ptr:已经不用了
  • unique_ptr:独占式指针,同一时刻只能有一个指针指向同一个对象
  • shared_ptr:共享式指针,同一时刻可以有多个指针指向同一个对象
  • weak_ptr:用来解决shared_ptr相互引用导致的死锁问题

回答2:

1.std::unique_ptr

所有权模型:独占所有权,同一时间只能有一个unique_ptr指向资源。

核心特点

  • 不可复制(delete了拷贝构造函数和拷贝赋值),但支持移动语义(std::move)。

  • 轻量级,性能接近裸指针。

使用场景

管理独占资源(如文件句柄、动态数组)。

替代new/delete,作为工厂函数的返回值

std::unique_ptr<int> p1 = std::make_unique<int>(42);
std::unique_ptr<int> p2 = std::move(p1); // 所有权转移
2.std::shared_ptr

所有权模型:共享所有权,通过引用计数管理资源。

核心特点
  • 多个shared_ptr可以指向同一资源,引用计数为0时释放资源。

  • 支持自定义删除器(如释放文件、网络连接等非内存资源)。
    潜在问题

潜在问题
  • 循环引用:若两个shared_ptr相互引用,引用计数无法归零,导致内存泄漏。
解决方案
auto p1 = std::make_shared<int>(42);
auto p2 = p1; // 引用计数+1
3.std::weak_ptr

所有权模型:弱引用,不增加引用计数。

核心作用:
  • 解决 shared_ptr 的循环引用问题。

  • 临时观察资源是否存在(通过lock()升级为shared_ptr)。

示例:
std::weak_ptr<int> wp = p1;
if (auto sp = wp.lock()) { // 若资源存在,升级为shared_ptr// 使用sp访问资源
}
智能指针的实现原理

unique_ptr

  • 封装裸指针,通过删除移动拷贝操作确保独占所有权。

shared_ptr

  • 包含两个指针:一个指向资源,一个指向控制块(含引用计数和删除器)。

  • 引用计数通过原子操作保证线程安全。

weak_ptr

  • 同样指向控制块,但不参与引用计数。
使用智能指针的最佳实践
优先使用make_shared和make_unique
auto p = std::make_shared<Object>(args); // 推荐
  • 避免显式new,提高异常安全性。

  • make_shared会合并资源与控制块的内存分配,提升性能。

避免混用裸指针和智能指针
int* raw_ptr = p.get(); // 谨慎使用get(),避免delete或延长生命周期。
自定义删除器
std::shared_ptr<FILE> file( fopen("test.txt", "r"), [](FILE* f) { fclose(f); } );

unique_ptr 能否被另一个 unique_ptr 拷贝呢

回答1:

不能,因为它把它的拷贝构造函数private了。但是它提供了一个移动构造函数,所以可以通过std::move将指针指向的对象交给另一个unique_ptr,转交之后自己就失去了这个指针对象的所有权,除非被显示交回

回答2:

为什么不能拷贝?
1.独占所有权:
  • std::unique_ptr 的核心设计是独占资源的所有权。如果允许拷贝,会导致多个指针指向同一资源,违背了独占所有权的原则。

  • 如果允许拷贝,资源释放时会出现重复释放的问题,导致未定义行为。

2.禁止拷贝构造函数和拷贝赋值运算符:
  • std::unique_ptr 的拷贝构造函数和拷贝赋值运算符被显式删除(= delete),因此无法直接拷贝。
std::unique_ptr<int> p1 = std::make_unique<int>(42);
std::unique_ptr<int> p2 = p1; // 错误:拷贝构造函数被删除
std::unique_ptr<int> p3;
p3 = p1; // 错误:拷贝赋值运算符被删除
2.如何转移所有权?

虽然 std::unique_ptr 不能拷贝,但可以通过移动语义(std::move)将资源的所有权从一个 std::unique_ptr 转移到另一个。

std::unique_ptr<int> p1 = std::make_unique<int>(42); // p1 拥有资源
std::unique_ptr<int> p2 = std::move(p1); // 将资源从 p1 转移到 p2if (p1 == nullptr) {std::cout << "p1 is now null, ownership transferred to p2." << std::endl;
}
std::move 的作用:

将 p1 的资源所有权转移到 p2。

转移后,p1 变为 nullptr,不再拥有资源。

总结
  • std::unique_ptr 不能被拷贝,因为它遵循独占所有权的语义。

  • 可以通过 std::move 转移所有权,转移后原指针变为 nullptr。

  • 这种设计确保了资源管理的安全性和明确性,避免了内存泄漏和重复释放的问题。

unique_ptr和shared_ptr的区别

unique_ptr

unique_ptr 是独占所有权的智能指针。它确保一个资源在任何时候只有一个 unique_ptr 拥有它。这意味着,unique_ptr 不允许多个指针指向同一个资源,资源的所有权不能被共享。

特点

  • 独占所有权:一个 unique_ptr 拥有资源的唯一所有权,不能被拷贝。只能通过 std::move() 转移所有权。
  • 内存自动释放:当 unique_ptr 被销毁时,它所管理的资源会自动释放。
  • 不可拷贝,但可以转移:unique_ptr 不能被拷贝,避免了资源的多重释放问题,但是可以通过 std::move() 转移所有权。
#include <iostream>
#include <memory>void uniquePtrExample() {// 创建 unique_ptr 并指向一个动态分配的整数std::unique_ptr<int> p1 = std::make_unique<int>(10);std::cout << "Value of p1: " << *p1 << std::endl;// 转移所有权std::unique_ptr<int> p2 = std::move(p1);// p1 现在是空指针if (!p1) {std::cout << "p1 is empty after move." << std::endl;}// p2 拥有资源std::cout << "Value of p2: " << *p2 << std::endl;
}

shared_ptr

shared_ptr 是共享所有权的智能指针。多个 shared_ptr 实例可以共享对同一资源的所有权。每当一个 shared_ptr 被拷贝时,资源的引用计数会增加。只有当所有指向该资源的 shared_ptr 都被销毁时,资源才会被释放。

特点
  • 共享所有权:多个 shared_ptr 可以共享同一个资源的所有权,资源的引用计数由 shared_ptr 内部自动管理。
  • 引用计数:每次拷贝一个 shared_ptr,引用计数增加,销毁一个 shared_ptr,引用计数减少。资源会在引用计数变为零时释放。
  • 线程安全:shared_ptr 是线程安全的,多个线程可以安全地使用同一个 shared_ptr。
#include <iostream>
#include <memory>void sharedPtrExample() {// 创建 shared_ptr 并指向一个动态分配的整数std::shared_ptr<int> p1 = std::make_shared<int>(20);std::cout << "Value of p1: " << *p1 << std::endl;// 创建另一个 shared_ptr 并共享资源std::shared_ptr<int> p2 = p1;std::cout << "Value of p2: " << *p2 << std::endl;// 引用计数为 2std::cout << "Reference count: " << p1.use_count() << std::endl;// p1 和 p2 都会在其生命周期结束时释放资源
}
主要区别总结
特性unique_ptrshared_ptr
所有权独占所有权,不能共享共享所有权,多个指针可以指向同一资源
拷贝不允许拷贝,只能转移所有权 (std::move)支持拷贝,引用计数增加
内存管理资源的释放由 unique_ptr 自动进行引用计数机制,所有 shared_ptr 被销毁时自动释放资源
性能性能较高,因为不涉及引用计数相较于 unique_ptr,性能较低,因为需要维护引用计数
线程安全非线程安全(不同线程中的 unique_ptr 不可共享)线程安全(引用计数是线程安全的,但资源访问需要加锁)
选择使用的场景
  • 使用 unique_ptr:适用于资源的唯一拥有者场景,能够提供高效、简洁的内存管理,不允许共享所有权。
    • 如:资源管理、文件句柄、网络连接等。
  • 使用 shared_ptr:适用于多个对象需要共享资源所有权的场景,例如资源共享、动态配置、对象池等。
    • 如:图形界面的窗口管理、图形对象共享、数据库连接池等。

总结

  • unique_ptr 是用来实现独占式所有权的智能指针,不能拷贝,可以转移所有权,适用于资源的唯一拥有者场景。
  • shared_ptr 是用来实现共享所有权的智能指针,可以被多个 shared_ptr 对象共享,并通过引用计数管理资源的生命周期,适用于多个对象共享资源的场景。

unique_ptr = unique_ptr 和 shared_ptr=shared_ptr这两个操作有什么后果呢?

回答1:

unique_ptr = unique_ptr :

unique_ptr 是独占所有权的智能指针,它不允许拷贝或赋值操作。直接执行 unique_ptr = unique_ptr 会导致编译错误。unique_ptr 的设计目的是确保资源只能有一个所有者,避免了资源的多重释放问题。

后果:

编译错误:因为 unique_ptr 不允许拷贝和赋值,只能通过 std::move() 转移所有权。假如你尝试执行 unique_ptr = unique_ptr,编译器会报错,提示无法拷贝 unique_ptr 对象。

  unique_ptr<int> p1 = make_unique<int>(10);unique_ptr<int> p2 = make_unique<int>(20);// 编译错误:unique_ptr 不允许拷贝或赋值p1 = p2;  // Error: unique_ptr不能直接赋值
解决方法:

unique_ptr 只能通过 std::move() 进行所有权转移。例如:

p1 = std::move(p2);  // p1 接管 p2 所有权,p2 被置为空
解释:

std::move(p2) 会把 p2 的所有权转移给 p1,此时 p2 成为一个空指针,不能再访问原来的资源。

shared_ptr = shared_ptr:

shared_ptr 是共享所有权的智能指针,允许多个 shared_ptr 对象共享同一个资源。在执行 shared_ptr = shared_ptr 操作时,引用计数会增加,即新的 shared_ptr 会共享原来指针的资源。

后果

正确: shared_ptr 允许多个 shared_ptr 指向同一个资源,通过引用计数管理资源的生命周期。当你将一个 shared_ptr 赋值给另一个时,引用计数会增加,表示更多的指针共享该资源。当所有指向该资源的 shared_ptr 被销毁时,资源才会被释放。

   shared_ptr<int> p1 = make_shared<int>(10);cout << "Reference count of p1: " << p1.use_count() << endl;  // 输出: 1shared_ptr<int> p2 = p1;  // 共享所有权,p1 和 p2 都指向同一个资源cout << "Reference count of p2: " << p2.use_count() << endl;  // 输出: 2
解释

当 p2 = p1 时,p1 和 p2 都指向同一个资源。此时,引用计数从 1 增加到 2,表示有两个 shared_ptr 对象共享资源。当这两个 shared_ptr 都被销毁时,资源才会被释放。

总结对比
  • unique_ptr = unique_ptr:编译错误,因为 unique_ptr 是独占所有权的,不允许拷贝或赋值。
    • 正确操作:通过 std::move() 转移所有权。
  • shared_ptr = shared_ptr:合法操作,引用计数增加,多个 shared_ptr 可以共享同一个资源,资源会在最后一个 shared_ptr 被销毁时释放。
实际应用
  • unique_ptr:适用于需要确保资源有唯一所有者的场景,避免了多重释放资源的问题。
  • shared_ptr:适用于多个对象共享资源的场景,自动管理资源的引用计数,确保资源在所有者全部销毁后才被释放。

shared_ptr的移动赋值时发生了什么事情

  • 首先它会检查本指针和参数指针是不是同一个对象,如果是,直接返回
  • 然后,先把本指针的引用变量–,如果发现减到了0,就把参数指针和参数引用变量析构掉并置NULL
  • 最后,本指针和参数指针指向同一个对象以及引用计数,然后引用计数自增1

shared_ptr什么时候会被释放掉,就是什么时候引用次数为0?

  • 在一个shared_ptr析构时,或者被赋值时,强引用计数会减1。减到0就delete资源

shared_ptr 你是怎么实现的?

shared_ptr 的核心实现基于 引用计数(Reference Counting)。它的基本原理是:

  1. 每个资源 由一个 shared_ptr 所管理,同时维护一个 引用计数(use_count)。
  2. 当一个新的 shared_ptr 复制已有的 shared_ptr ,引用计数增加。
  3. 当一个 shared_ptr 被销毁时,引用计数减少。
  4. 当引用计数归零时,释放所管理的资源。

自己实现一个 shared_ptr

简化版的 shared_ptr,主要包含:

  • 资源指针 (T* ptr)
  • 引用计数 (int* count)
  • 拷贝构造 & 赋值操作(增加引用计数)
  • 析构函数(减少引用计数,归零时释放资源)
#include <iostream>template <typename T>
class MySharedPtr {
private:T* ptr;       // 资源指针int* count;   // 引用计数public:// **构造函数**explicit MySharedPtr(T* p = nullptr) : ptr(p) {// ptr 指向新建资源// count 记录资源的引用次数if (ptr) {count = new int(1);  // 初始化引用计数为 1} else {count = new int(0);}}// **拷贝构造函数(增加引用计数)**  -> 复制 ptr 和 count,即共享资源MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), count(other.count) {if (ptr) {(*count)++;  // 增加引用计数}}// **赋值运算符(处理自赋值 & 管理引用计数)**MySharedPtr& operator=(const MySharedPtr& other) {if (this == &other)  // 避免自赋值return *this;// 先释放当前对象的资源release();// 复制新对象ptr = other.ptr;count = other.count;if (ptr) {(*count)++;  // 增加引用计数}return *this;}// **释放资源(减少引用计数)**void release() {if (ptr && --(*count) == 0) { // 引用计数归零时释放资源delete ptr;delete count;}}// **析构函数**~MySharedPtr() {release();}// **获取原始指针**T* get() const { return ptr; }// **重载 `->` 和 `*` 以便像指针一样使用**T* operator->() const { return ptr; }T& operator*() const { return *ptr; }// **获取引用计数**int use_count() const { return *count; }
};// **测试 `MySharedPtr`**
int main() {MySharedPtr<int> p1(new int(42));std::cout << "p1 count: " << p1.use_count() << std::endl; // 1MySharedPtr<int> p2 = p1; // 复制 p1std::cout << "p1 count after copy: " << p1.use_count() << std::endl; // 2std::cout << "p2 count: " << p2.use_count() << std::endl; // 2{MySharedPtr<int> p3 = p2; // 复制 p2std::cout << "p3 count: " << p3.use_count() << std::endl; // 3} // p3 作用域结束,引用计数减少std::cout << "p1 count after p3 destroyed: " << p1.use_count() << std::endl; // 2return 0;
}

shared_ptr是不是线程安全?

结论

std::shared_ptr 本身是线程安全的,但它管理的对象 不是 线程安全的。

线程安全的部分
1. 引用计数的增加/减少是原子操作
  • std::shared_ptr 内部使用了 原子操作 来管理引用计数(std::atomic 或等效实现)。
  • 这意味着多个线程可以同时 拷贝销毁 shared_ptr,不会导致未定义行为。
  • 例如,多个线程可以同时调用 std::shared_ptr p1 = p2; 或 p1.reset();,不会导致引用计数错误。
线程不安全的部分
1.共享的对象本身不是线程安全的
  • shared_ptr 只是管理了对象的生命周期,但不提供对象访问的同步保护。
  • 如果多个线程同时 读取/修改 shared_ptr 所管理的对象,而没有额外的同步机制(如 std::mutex),会导致 数据竞争 和 未定义行为。
std::shared_ptr<int> p = std::make_shared<int>(42);
std::thread t1([&]() { *p = 10; });  // 修改值
std::thread t2([&]() { std::cout << *p << std::endl; });  // 读取值
t1.join();
t2.join();

上面的代码 可能会崩溃 或 输出未定义结果,因为 *p 被多个线程同时访问,没有同步保护。

2.不同 shared_ptr 实例对同一对象的修改是线程不安全的

例如:

std::shared_ptr<int> p1 = std::make_shared<int>(42);
std::shared_ptr<int> p2 = p1;  // 线程安全(增加引用计数)
std::thread t1([&]() { p1.reset(); });  // 线程1释放对象
std::thread t2([&]() { p2.reset(); });  // 线程2释放对象

这虽然不会导致引用计数错误,但如果 reset() 操作后仍然访问 p1 或 p2,可能导致 访问已释放的对象(悬垂指针)。

如何保证线程安全?
  1. 只在单个线程中访问 shared_ptr 管理的对象
  2. 使用 std::mutex 保护对象访问
std::mutex mtx;
std::shared_ptr<int> p = std::make_shared<int>(42);std::thread t1([&]() {std::lock_guard<std::mutex> lock(mtx);*p = 10;
});std::thread t2([&]() {std::lock_guard<std::mutex> lock(mtx);std::cout << *p << std::endl;
});t1.join();
t2.join();
3.使用 std::atomic<std::shared_ptr>

std::atomic<std::shared_ptr> 允许线程安全地 更新 shared_ptr(但仍不保证管理的对象是线程安全的)。

std::atomic<std::shared_ptr<int>> atomic_p(std::make_shared<int>(42));
std::thread t1([&]() { atomic_p.store(std::make_shared<int>(100)); });
std::thread t2([&]() { std::cout << *atomic_p.load() << std::endl; });
t1.join();
t2.join();

这样可以保证 shared_ptr 本身的更新是安全的,但仍然需要同步对象访问。

总结

线程安全性shared_ptr
拷贝/赋值/销毁√ 线程安全(原子操作)
管理的对象访问× 线程不安全(需要同步)
多个 shared_ptr 指向同一对象√ 线程安全(引用计数安全)
多个线程同时修改 shared_ptr× 线程不安全(需 std::mutex 或 std::atomic)

如果你在多线程环境下使用 shared_ptr,确保:

  • 线程不会同时修改管理的对象,或者使用 std::mutex 保护访问。
  • 需要线程安全的 shared_ptr 赋值时,可以使用 std::atomic<std::shared_ptr>。

sharedPtr在64位操作系统下大小有多大

  • 两个指针,64字节

shared_ptr和weak_ptr之间是什么关系?

weak_ptr是用来辅助shared_ptr的,每一个weak_ptr它指向weak_ptr而不是实际的操作函数。
std::weak_ptr 是 std::shared_ptr 的 弱引用,它和 shared_ptr 之间有以下关系

1. weak_ptr 解决 shared_ptr 的循环引用问题

问题:shared_ptr 可能导致循环引用,导致内存泄漏
当两个 shared_ptr 相互引用时,它们的引用计数不会降为 0,导致对象无法释放。例如:

#include <iostream>
#include <memory>class B;  // 前置声明class A {
public:std::shared_ptr<B> b_ptr;~A() { std::cout << "A 被销毁\n"; }
};class B {
public:std::shared_ptr<A> a_ptr;~B() { std::cout << "B 被销毁\n"; }
};int main() {auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;  // 循环引用return 0;  // 内存泄漏:A 和 B 都不会被销毁
}

解决方案:用 weak_ptr 代替 shared_ptr 解决循环引用
weak_ptr 不会增加引用计数,所以 shared_ptr 之间不会出现循环依赖:

class B;  // 前置声明class A {
public:std::weak_ptr<B> b_ptr;  // 改为 weak_ptr~A() { std::cout << "A 被销毁\n"; }
};class B {
public:std::shared_ptr<A> a_ptr;~B() { std::cout << "B 被销毁\n"; }
};int main() {auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->b_ptr = b;  // 现在是 weak_ptrb->a_ptr = a;return 0;  // A 和 B 都能正常释放
}

2. weak_ptr 不影响 shared_ptr 的引用计数

  • std::weak_ptr 不会增加引用计数(use_count() 不变)
  • std::weak_ptr 只持有对象的弱引用,如果对象已经被释放,weak_ptr 变成 空指针
#include <iostream>
#include <memory>int main() {std::shared_ptr<int> sp = std::make_shared<int>(42);std::weak_ptr<int> wp = sp;  // 不增加引用计数std::cout << "use_count: " << sp.use_count() << std::endl;  // 输出 1sp.reset();  // 释放 shared_ptrif (wp.expired()) {std::cout << "对象已被销毁\n";  // wp 变为空指针}return 0;
}

3. weak_ptr 需要 lock() 访问对象

weak_ptr 不能直接访问对象,要用 lock() 获取 shared_ptr,然后再使用:

std::weak_ptr<int> wp;{std::shared_ptr<int> sp = std::make_shared<int>(42);wp = sp;  // 赋值,不增加引用计数
}if (std::shared_ptr<int> sp = wp.lock()) {  // 获取 shared_ptrstd::cout << *sp << std::endl;
} else {std::cout << "对象已被销毁\n";
}

为什么推荐用makeshared创建指针?

1. 避免额外的内存分配,提高性能

使用 new 创建 shared_ptr,需要 两次内存分配

  1. 分配对象的内存 (new T)
  2. 分配 shared_ptr 的控制块(用于存储引用计数)

但 std::make_shared 只分配一次内存,控制块和对象存在同一块内存区域,减少了额外的分配和释放操作,提高性能。

std::shared_ptr<int> sp1(new int(42)); // 两次分配(对象 + 控制块)
std::shared_ptr<int> sp2 = std::make_shared<int>(42); // 一次分配(对象+控制块一起分配)

结论: make_shared 比 new + shared_ptr 方式更快、更高效。

2. 避免异常安全问题

如果 new 之后发生异常,可能导致 内存泄漏。

错误示例(可能泄漏内存):
std::shared_ptr<int> sp1(new int(42));  // new 成功后,sp1 可能抛出异常
  • new int(42) 先执行
  • std::shared_ptr 的构造函数可能抛异常(比如 bad_alloc)
  • new 分配的内存不会被释放,造成泄漏
正确做法(异常安全):
std::shared_ptr<int> sp1 = std::make_shared<int>(42);

原因: make_shared 直接分配对象并初始化 shared_ptr,不会导致内存泄漏。

3. 减少 shared_ptr 的构造开销

使用 new 方式:

auto sp1 = std::shared_ptr<int>(new int(42));  // 两步:new + shared_ptr 构造

使用 make_shared 方式:

auto sp2 = std::make_shared<int>(42);  // 直接分配和初始化

make_shared 更高效,代码更简洁

4. make_shared 允许数组初始化

C++17 之后,make_shared 支持数组:

auto sp = std::make_shared<int[]>(10);  // 分配 10 个 int

结论:

推荐用 std::make_shared,因为它:
✅ 性能更高(减少一次内存分配)
✅ 避免异常安全问题(不会导致内存泄漏)
✅ 代码更简洁(不需要 new)
✅ 减少 shared_ptr 构造开销

⚠️ 但如果 shared_ptr 需要自定义删除器,则必须手动 new,不能用 make_shared。

为什么要用 shared_from_this?

什么是 shared_from_this

shared_from_this 是 std::enable_shared_from_this 提供的一个方法,允许 一个类的成员函数安全地获取自身的 shared_ptr。

为什么要用 shared_from_this?

如果一个对象已经被 std::shared_ptr 管理,直接用 this 构造新的 shared_ptr 会导致严重错误(多重 shared_ptr 造成对象被提前释放)。

错误示例(手动构造 shared_ptr 导致对象重复释放):
class MyClass {
public:void DangerousMethod() {// ❌ 直接创建 shared_ptr,会导致双重管理std::shared_ptr<MyClass> sp_this(this);}
};int main() {std::shared_ptr<MyClass> sp1 = std::make_shared<MyClass>();sp1->DangerousMethod(); // ⚠️ sp1 和 sp_this 现在都在管理同一个对象return 0;
}  // ❌ 可能导致对象被删除两次(double free)

问题:

  • sp1 和 sp_this 都认为自己是 唯一的 shared_ptr,它们会各自删除对象,导致 double free(重复释放) 或 段错误。
解决方案:使用 shared_from_this
#include <iostream>
#include <memory>class MyClass : public std::enable_shared_from_this<MyClass> {
public:std::shared_ptr<MyClass> SafeMethod() {return shared_from_this(); // ✅ 返回正确的 shared_ptr}
};int main() {std::shared_ptr<MyClass> sp1 = std::make_shared<MyClass>();std::shared_ptr<MyClass> sp2 = sp1->SafeMethod(); // ✅ 共享同一个控制块std::cout << "sp1.use_count() = " << sp1.use_count() << std::endl; // 输出 2return 0;
}

为什么 shared_from_this() 安全?

  • shared_from_this() 不会创建新的控制块,而是返回现有的 shared_ptr,保证同一个对象只有一个引用计数管理。

shared_from_this 适用场景

  1. 对象需要在方法内部创建 shared_ptr,但不能改变已有的 shared_ptr 计数
  2. 观察者模式(Observer Pattern)
    • 监听者持有 shared_ptr,如果通知时直接用 this 可能导致悬挂指针
  3. 回调(Callback)
    • 避免 std::bind 或 std::function 误用 this,导致悬挂指针
  4. 多线程任务调度
  • 任务可能在 shared_ptr 作用域外完成,确保对象存活

总结

✅ shared_from_this() 确保 shared_ptr 共享相同的控制块,避免重复管理对象
✅ 防止 this 直接构造 shared_ptr 造成的内存错误
✅ 适用于回调、观察者模式、多线程等场景
⚠️ 使用 shared_from_this 的类必须继承 std::enable_shared_from_this

weak_ptr的原理

std::weak_ptr 是 std::shared_ptr 的辅助指针,它不会影响引用计数,主要用于 解决循环引用问题 和 临时访问 shared_ptr 管理的对象。

weak_ptr 的底层原理
  1. weak_ptr 和 shared_ptr 共享同一个控制块(Control Block)
    • shared_ptr 内部有 引用计数(strong count) 和 弱引用计数(weak count)。
    • weak_ptr 仅增加 弱引用计数,不会影响 强引用计数。
  2. weak_ptr 不能直接使用对象,需要 lock() 转换为 shared_ptr
    • weak_ptr 不能直接访问资源,因为可能已经被释放。
    • 通过 lock() 方法获得 shared_ptr,如果对象已销毁,lock() 返回 nullptr。
底层数据结构(简化示意图)
struct ControlBlock {int strong_count;  // 强引用计数(shared_ptr 持有)int weak_count;    // 弱引用计数(weak_ptr 持有)void* ptr;         // 指向对象的指针
};
总结

✅ weak_ptr 不增加引用计数,避免循环引用问题
✅ lock() 方法用于安全访问对象,防止 dangling pointer(悬挂指针)
✅ 适用于缓存、观察者模式、循环引用等场景
⚠️ weak_ptr 不能直接使用,需要 lock() 转换成 shared_ptr

weak_ptr的使用场景

1. weak_ptr 解决循环引用

#include <iostream>
#include <memory>class B; // 前向声明class A {
public:std::shared_ptr<B> ptrB;~A() { std::cout << "A destroyed\n"; }
};class B {
public:std::weak_ptr<A> ptrA; // ✅ 使用 weak_ptr 避免循环引用~B() { std::cout << "B destroyed\n"; }
};int main() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->ptrB = b;b->ptrA = a; // ✅ 这里使用 weak_ptr,否则 A 和 B 互相引用,永远不会释放return 0; // ✅ A 和 B 都会被正确销毁
}

如果 ptrA 也是 shared_ptr,会发生内存泄漏

class B {
public:std::shared_ptr<A> ptrA; // ❌ 这样会导致循环引用,内存永远无法释放
};

2. lock() 获取 shared_ptr

#include <iostream>
#include <memory>class MyClass {
public:void show() { std::cout << "Object is alive!\n"; }
};int main() {std::shared_ptr<MyClass> sp = std::make_shared<MyClass>();std::weak_ptr<MyClass> wp = sp; // ✅ 创建 weak_ptrif (auto shared = wp.lock()) { // ✅ 转换成 shared_ptrshared->show();} else {std::cout << "Object already deleted\n";}sp.reset(); // ❌ 释放对象if (auto shared = wp.lock()) {shared->show();} else {std::cout << "Object already deleted\n"; // ✅ 这里会输出}
}

weak_ptr 适用场景

  1. 解决 shared_ptr 循环引用
    • 互相引用的对象,需要一个使用 weak_ptr 避免内存泄漏。
  2. 缓存(Cache)
    • 避免缓存对象始终存活,但仍然可以被共享使用。
  3. 观察者模式(Observer Pattern)
    • 监听者使用 weak_ptr 避免对象一直存活。

关于shared_ptr相互引用具体讲一下

weak_ptr 是为了解决 shared_ptr 双向引用的问题。即:

class B;
struct A{shared_ptr<B> b;
};
struct B{shared_ptr<A> a;
};
auto pa = make_shared<A>();
auto pb = make_shared<B>();
pa->b = pb;
pb->a = pa;

pa 和 pb 存在着循环引用,根据 shared_ptr 引用计数的原理,pa 和 pb 都无法被正常的释放。
对于这种情况, 我们可以使用 weak_ptr:

class B;
struct A{shared_ptr<B> b;
};
struct B{weak_ptr<A> a;
};
auto pa = make_shared<A>();
auto pb = make_shared<B>();
pa->b = pb;
pb->a = pa;

weak_ptr 不会增加引用计数,因此可以打破 shared_ptr 的循环引用。
通常做法是 parent 类持有 child 的 shared_ptr, child 持有指向 parent 的 weak_ptr。这样也更符合语义。

weak_ptr如何检测指针是否被销毁

std::weak_ptr 可以通过 expired() 或 lock() 方法来检测管理的对象是否已经被销毁。

方法1:expired()

  • 如果对象已经被销毁,返回 true。
  • 如果对象仍然存活,返回 false。
#include <iostream>
#include <memory>class MyClass {
public:void show() { std::cout << "Object is alive!\n"; }
};int main() {std::weak_ptr<MyClass> wp; // 创建 weak_ptr{std::shared_ptr<MyClass> sp = std::make_shared<MyClass>(); // 创建 shared_ptrwp = sp; // weak_ptr 观察 shared_ptrstd::cout << "Object still exists: " << std::boolalpha << !wp.expired() << "\n";} // `sp` 离开作用域,对象被销毁std::cout << "Object still exists: " << std::boolalpha << !wp.expired() << "\n"; // ✅ 这里会输出 falsereturn 0;
}

输出:

Object still exists: true
Object still exists: false

方法 2:lock()

  • lock() 返回一个新的 shared_ptr:
    • 如果对象仍然存活,返回有效的 shared_ptr。
    • 如果对象已经销毁,返回空的 shared_ptr(nullptr)。
#include <iostream>
#include <memory>class MyClass {
public:void show() { std::cout << "Object is alive!\n"; }
};int main() {std::weak_ptr<MyClass> wp; // 创建 weak_ptr{std::shared_ptr<MyClass> sp = std::make_shared<MyClass>(); // 创建 shared_ptrwp = sp; // weak_ptr 观察 shared_ptrif (auto sp2 = wp.lock()) { // 尝试获取 shared_ptrsp2->show();} else {std::cout << "Object already deleted\n";}} // `sp` 离开作用域,对象被销毁if (auto sp2 = wp.lock()) {sp2->show();} else {std::cout << "Object already deleted\n"; // ✅ 这里会输出}return 0;
}

输出:

Object is alive!
Object already deleted

结论

方法用途适用场景
expired()检查对象是否销毁只想知道对象是否还活着,不需要访问对象
lock()获取 shared_ptr 访问对象需要访问对象,并避免悬挂指针

建议:

只检查对象是否存活时,使用 expired()。
需要访问对象时,使用 lock(),防止访问已销毁对象导致未定义行为

如何将weak_ptr转换为shared_ptr

在 C++ 中,可以使用 weak_ptr.lock() 方法将 weak_ptr 转换为 shared_ptr。

  • 如果对象仍然存在,lock() 返回一个有效的 shared_ptr,对象的引用计数会增加。
  • 如果对象已被销毁,lock() 返回一个空的 shared_ptr(nullptr)。
#include <iostream>
#include <memory>class MyClass {
public:void show() { std::cout << "Object is alive!\n"; }
};int main() {std::weak_ptr<MyClass> wp;  // 创建 weak_ptr{std::shared_ptr<MyClass> sp = std::make_shared<MyClass>(); // 创建 shared_ptrwp = sp; // weak_ptr 观察 shared_ptr// 使用 lock() 转换为 shared_ptrstd::shared_ptr<MyClass> sp2 = wp.lock();if (sp2) {sp2->show();} else {std::cout << "Object already deleted\n";}} // sp 离开作用域,对象被销毁// 再次尝试转换std::shared_ptr<MyClass> sp3 = wp.lock();if (sp3) {sp3->show();} else {std::cout << "Object already deleted\n";  // ✅ 这里会输出}return 0;
}Object is alive!
Object already deleted

lock() 的作用

1. 防止悬挂指针:
  • weak_ptr 本身不能直接访问对象,但 lock() 可以安全地检查对象是否存在,并返回 shared_ptr。
2. 增加引用计数:
  • lock() 成功获取 shared_ptr 后,对象的引用计数会增加,确保对象在作用域内不会被释放。

注意

不要直接使用 expired() + lock(),这种做法可能会引发竞态条件:

if (!wp.expired()) {std::shared_ptr<MyClass> sp = wp.lock();  // 可能 wp 在这里已经失效
}

正确做法:直接用 lock()

if (auto sp = wp.lock()) { // 只有 sp 有效时才访问对象sp->show();
}

总结

  • 使用 lock() 来转换 weak_ptr 为 shared_ptr,确保对象存在。
  • 避免 expired() + lock(),因为会有竞态条件。
  • 适用于缓存、观察者模式等场景,防止悬挂指针。

相关文章:

C/C++ 面试智能指针

说下你对智能指针的理解 回答1: 因为C使用内存的时候很容易出现野指针、悬空指针、内存泄露的问题。所以C11引入了智能指针来管理内存。有四种&#xff1a; auto_ptr&#xff1a;已经不用了unique_ptr&#xff1a;独占式指针&#xff0c;同一时刻只能有一个指针指向同一个对…...

Halcon缓存?内存泄漏?

目录 1、前言 2、图片缓存 3、全局内存缓存 4、临时内存缓存 5、处理 HALCON 中的疑似内存泄漏 6、其他 1、前言 除⾮必要,否则不建议修改 HALCON 自带的缓存设置。 2、图片缓存 图像通常需要大量内存,而分配大块内存的过程较慢。因此,当释放图像时,HALCON并…...

升级 SpringBoot3 全项目讲解 — 周边店铺展示功能如何实现

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; 1. 升级 Spring Boot 到 3.x 在升级 Spring Boot 之前&#xff0c;我们需要确保项目的依赖和配置与新版本兼容。以下是升级的主要步骤&#xff1a; 1.1 更新 pom.xml 文件 首先&#…...

Git(分布式版本控制系统)系统学习笔记【并利用腾讯云的CODING和Windows上的Git工具来实操】

Git的概要介绍 1️⃣ Git 是什么&#xff1f; Git 是一个 分布式版本控制系统&#xff08;DVCS&#xff09;&#xff0c;用于跟踪代码的变更、协作开发和管理项目历史。 由 Linus Torvalds&#xff08;Linux 之父&#xff09;在 2005 年开发&#xff0c;主要用于 代码管理。…...

光学和光子学模拟工具在 AR/VR 中的作用

AR/VR 中的光学和光子学 增强现实 (AR) 和虚拟现实 (VR) 站在数字进化的前沿。光学和光子学这一复杂的科学深入研究了光的产生、检测和操控&#xff0c;在这一转变中发挥着至关重要的作用。 图 1 (a) 展示了 AR 系统的设计&#xff0c;强调了光学的关键作用。该图描绘了光的旅…...

大模型产品Deepseek(四)、本地安装部署(Ollama方式)

Ollama与DeepSeek的本地安装与部署教程(Windows/MacOS) 在许多AI应用场景中,您可能希望将智能模型本地化,以便更高效地处理数据并减少对外部云服务的依赖。本文将介绍如何在Windows和macOS上直接安装和配置Ollama,以及如何基于Ollama平台部署DeepSeek模型并进行本地交互。…...

visual studio安装

一、下载Visual Studio 访问Visual Studio官方网站。下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux 在主页上找到并点击“下载 Visual Studio”按钮。 选择适合需求的版本&#xff0c;例如“Visual Studio Community”&#xff08;免费版本&#xff09;&#x…...

AI大模型——DeepSeek模型部署实战

摘要 文章主要介绍了DeepSeek大模型的本地部署方法、使用方式以及API接入相关内容。首先指出可通过下载Ollama来部署DeepSeek-R1模型&#xff0c;并给出了模型不同参数版本及存储信息。接着说明了如何通过Chatbox官网下载并接入DeepSeek API&#xff0c;以及如何接入本地部署模…...

音视频的文件封装——AVI、MP4、MKV

3.MKV (Matroska Video File) Matroska &#xff08;俄语&#xff1a; матроска &#xff09;是一种多媒体封装格式&#xff0c;可把多种不同编码的影像、不同格式的音频、不同语言的字幕封装到一个文件内。也是一种开放源代码的多媒体封装格式。 Matroska 支持多种文件…...

讯飞绘镜(ai生成视频)技术浅析(五):视频生成

讯飞绘镜(AI生成视频)是一种先进的AI视频生成技术,能够将静态的分镜画面转换为动态视频,并使画面中的元素按照一定的逻辑和动作进行动态展示。 一、讯飞绘镜视频生成技术概述 讯飞绘镜的视频生成技术主要包含以下几个核心模块: 1.视频生成模型:包括生成对抗网络(GAN)…...

【FPGA】 MIPS 12条整数指令 【3】

实现乘除 修改框架 EX&#xff1a;实现带符号乘除法和无符号乘除法 HiLo寄存器&#xff1a;用于存放乘法和除法的运算结果。Hi、Lo为32bit寄存器。电路描述与实现RegFile思想一致 仿真 代码 DataMem.v include "define.v"; module DataMem(input wire clk,input…...

【补充】RustDesk一键部署及账号登录配置

前言 之前分享的配置rustdesk的帖子只是搭建了一个简易服务器&#xff0c;仅能实现简单的远程桌面功能。在后续的使用中切换设备使用时无法看到之前连接的设备&#xff0c;必须知道每个设备的id号&#xff0c;才能在新设备上连接。数据无法在设备间迁移&#xff0c;感觉很麻烦…...

2025.2.6 数模AI智能体大更新,更专业的比赛辅导,同提示词效果优于gpt-o1/o3mini、deepseek-r1满血

本次更新重新梳理了回复逻辑规则&#xff0c;无任何工作流&#xff0c;一共3.2k字细节描述。具体效果可以看视频&#xff0c;同时也比对了gpt-o1、gpt-o3mini、deepseek-r1-67BI&#xff0c;从数学建模题目解答上来看&#xff0c;目前我的数模AI智能体具有明显优势。 AI智能体优…...

昇腾,Ascend,NPU,mindie,镜像,部署vllm:第3篇,补档,没事可以看看这个readme

文章目录 前言文件位置已改变atb model和mindie的关系前言 在之前的版本中,我们提到了一个帮助文档(Readme),这个文档告诉我们,当时的mindie版本不支持0.5b版本的通义千问2.5 我现在就很好奇,新版的mindie,是否支持0.5b的版本呢? 文件位置已改变 之前的文件位置: …...

PbootCMS 修改跳转提示,修改笑脸时间

在使用时&#xff0c;每次都提示这个&#xff1a; 修改方法&#xff1a; 修改跳转时间&#xff1a;找到 handle.php 文件编辑 &#xff0c;调整 setTimeout 函数的时间参数。 修改提示文字&#xff1a;编辑 handle.php 文件&#xff0c;修改提示文字的内容。 隐藏提示页面&am…...

Python办公笔记——将csv文件转Json

目录 专栏导读1、背景2、库的安装3、代码1—自定义表头4、代码2—全字段5、代码3—全字段总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——> 一晌…...

25/2/7 <机器人基础>雅可比矩阵计算 雅可比伪逆

雅可比矩阵计算 雅可比矩阵的定义 假设我们有一个简单的两个关节的平面机器人臂&#xff0c;其末端执行器的位置可以表示为&#xff1a; 其中&#xff1a; L1​ 和 L2 是机器人臂的长度。θ1​ 和 θ2是关节的角度。 计算雅可比矩阵 雅可比矩阵 JJ 的定义是将关节速度与末…...

流媒体缓存管理策略

缓存管理策略是指为了优化性能、资源使用和用户体验而对缓存数据进行管理的方法和规则。以下是一些常见的缓存管理策略&#xff1a; 1. LRU&#xff08;Least Recently Used&#xff0c;最近最少使用&#xff09; 原理&#xff1a;当缓存满了&#xff0c;需要腾出空间时&…...

【Sql Server】sql server 2019设置远程访问,外网服务器需要设置好安全组入方向规则

大家好&#xff0c;我是&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言1、无法链接2、数…...

阿里云 | DeepSeek人工智能大模型安装部署

ModelScope是阿里云人工智能大模型开源社区 ModelScope网络链接地址 https://www.modelscope.cn DeepSeek模型库网络链接地址 https://www.modelscope.cn/organization/deepseek-ai 如上所示&#xff0c;在阿里云人工智能大模型开源社区ModelScope中&#xff0c;使用阿里云…...

Spring Test 教程大纲

Spring Test 教程大纲 目标&#xff1a;基于 JUnit 5&#xff0c;系统学习 Spring Test 的核心原理、注解使用、测试框架集成及扩展开发。 第 1 章&#xff1a;Spring Test 简介与核心原理 1.1 Spring Test 的作用与优势 简化 Spring 应用的单元测试与集成测试。核心目标&am…...

MySQL数据库基础(创建/删除 数据库/表)

一、数据库的操作 1.1 显示当前数据库 语法&#xff1a;show databases&#xff1b; <1>show 是一个关键字&#xff0c;表示要执行的操作类型 <2>databases 是复数&#xff0c;表示显示所有数据库 上面的数据库中&#xff0c;除了java113&#xff0c;其它的数据库…...

Llama最新开源大模型Llama3.1

Meta公司于2024年7月23日发布了最新的开源大模型Llama 3.1&#xff0c;这是其在大语言模型领域的重要进展。以下是关于Llama 3.1的详细介绍&#xff1a; 参数规模与训练数据 Llama 3.1拥有4050亿&#xff08;405B&#xff09;参数&#xff0c;是目前开源领域中参数规模最大的…...

PHP 面向对象编程详解

PHP 面向对象编程详解 引言 PHP 作为一种广泛使用的服务器端脚本语言&#xff0c;自诞生以来就以其简洁、易学、高效的特点受到开发者的喜爱。随着互联网技术的不断发展&#xff0c;PHP 也在不断地进化&#xff0c;其中面向对象编程&#xff08;OOP&#xff09;已经成为 PHP …...

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-files.py

files.py ultralytics\utils\files.py 目录 files.py 1.所需的库和模块 2.class WorkingDirectory(contextlib.ContextDecorator): 3.def spaces_in_path(path): 4.def increment_path(path, exist_okFalse, sep"", mkdirFalse): 5.def file_age(path__fi…...

安卓开发,打开PDF文件

1、把PDF文件复制到raw目录下 &#xff08;1&#xff09;新建一个Android Resource Directory (2)Resource type 改成 raw (3) 把PDF文件复制到raw目录下 2、activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayoutxmlns:and…...

标准模版——添加蜂鸣器及继电器功能模块

一、peripheral.h代码 #include "STC15F2K60S2.H"void Set_Peripheral(unsigned char Buzzer_Status,unsigned char Relay_Status); 二、peripheral.c代码 #include "peripheral.h"void Set_Peripheral(unsigned char Buzzer_Status,unsigned char Rela…...

嵌入式工程师面试经验分享与案例解析

嵌入式工程师岗位受到众多求职者的关注。面试流程严格&#xff0c;技术要求全面&#xff0c;涵盖C/C编程、数据结构与算法、操作系统、嵌入式系统开发、硬件驱动等多个方向。本文将结合真实案例&#xff0c;深入剖析嵌入式工程师的面试流程、常见问题及应对策略&#xff0c;帮助…...

《云夹:高效便捷的书签管理利器》

在信息爆炸的时代&#xff0c;我们每天都会浏览大量的网页&#xff0c;遇到许多有价值的内容。如何高效地管理这些网页书签&#xff0c;以便随时快速访问&#xff0c;成为了一个重要的问题。云夹作为一款出色的书签管理工具&#xff0c;为我们提供了完美的解决方案。 强大的功能…...

ASP.NET Core标识框架Identity

目录 Authentication与Authorization 标识框架&#xff08;Identity&#xff09; Identity框架的使用 初始化 自定义属性 案例一&#xff1a;添加用户、角色 案例二&#xff1a;检查登录用户信息 案例三&#xff1a;实现密码的重置 步骤 Authentication与Authorizatio…...

Web - CSS3过渡与动画

过渡 基本使用 transition过渡属性是css3浓墨重彩的特性&#xff0c;过渡可以为一个元素在不同样式之间变化自动添加补间动画。 过渡从kIE10开始兼容&#xff0c;移动端兼容良好&#xff0c;网页上的动画特效基本都是由JavaScript定时器实现的&#xff0c;现在逐步改为css3过…...

css-根据不同后端返回值返回渲染不同的div样式以及公共组件设定

1.动态绑定 Vue: 使用计算属性 getClassName 来动态计算样式类名&#xff0c;并通过 :class 绑定到 div 元素上。 <template><div :class"getClassName">这是一个根据后端值动态设置样式的 div 元素。</div> </template><script> exp…...

ES6 Set 数据结构用法总结

1. Set 基本概念 Set 是 ES6 提供的新的数据结构&#xff0c;类似于数组&#xff0c;但成员的值都是唯一的&#xff0c;没有重复的值。Set 本身是一个构造函数&#xff0c;用来生成 Set 数据结构。 1.1 基本用法 // 创建一个空Set const set new Set();// 创建一个带有初始…...

Celery任务阻塞问题排查

笔者在工作中经常用到Celery&#xff0c;遇到了2个任务阻塞的问题&#xff0c;分享经验如下。 1 Celery原理 Celery是基于Python开发的分布式任务调度框架&#xff0c;可以将任务发送到若干台机器上&#xff0c;实现多并发调度和计算。Celery的架构主要包含生产者&#xff08…...

巧用DeepSeek,编写CAPL自动化测试脚本

文章目录 前言提问及回答小结 前言 蛇年伊始&#xff0c;火出圈的除了《哪吒2》登顶中国影史票房第一外&#xff0c;科技圈的DeepSeek国产大模型引爆全球&#xff0c;关于在DeepSeek上的提问无奇不有。就车载通信自动化测试&#xff0c;本文也来蹭蹭热度。作为CAN/LIN协议一致…...

【信息系统项目管理师】第21章:项目管理科学基础 详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 21.1 工程经济学21.2 运筹学1.线性规划2.一般的运输问题3.指派问题4.动态规划法5.最小生成树6.博弈论7.决策每年管理科学在综合题里固定是5分,案例分析和论文不考。主要是科学管理相关内容,包括工程经济学、运…...

webpack配置语言之---ts

由于webpack本身不支持ts&#xff0c;如果需要编译ts文件&#xff0c;需要下载相应的loader对ts文件进行编译&#xff0c;以及配置tsconfig.json文件&#xff0c;配置ts的编译选项 1. 安装必要的依赖 首先&#xff0c;确保你已经安装了 Webpack 和 TypeScript。如果还没有安装…...

WPF 进度条(ProgressBar)示例一

本文讲述&#xff1a;WPF 进度条(ProgressBar)简单的样式修改和使用。 进度显示界面&#xff1a;使用UserControl把ProgressBar和进度值以及要显示的内容全部组装在UserControl界面中&#xff0c;方便其他界面直接进行使用。 <UserControl x:Class"DefProcessBarDemo…...

金蛇祈福,鸿运开年!广州白云皮具城2025开市大吉!

锣鼓一响&#xff0c;黄金万两&#xff01;2月6日大年初九&#xff0c;广州白云皮具城举行盛大的醒狮开市仪式&#xff01;象征吉祥如意的醒狮&#xff0c;将好运、财运传递给全体商户和八方来客。 醒狮点睛 金鼓一响黄金万两&#xff0c;十头醒狮登台&#xff0c;董事总经理刘…...

centos虚拟机迁移没有ip的问题

故事背景&#xff0c;我们的centos虚拟机本来是好好的&#xff0c;但是拷贝到其他电脑上就不能分配ip&#xff0c;我个人觉得这个vmware他们软件应该搞定这个啊&#xff0c;因为这个问题是每次都会出现的。 网络选桥接 网络启动失败 service network restart Restarting netw…...

【ESP32cam人脸识别开门及服务器端实战源码】

本项目实现了一个基于ESP32-CAM的实时人脸识别系统&#xff0c;能够通过WiFi进行视频流传输&#xff0c;并在检测到人脸时触发开门指令。系统由两个主要部分组成&#xff1a;video.py&#xff08;后端服务器&#xff09;和 ESP32-CAM.ino&#xff08;ESP32-CAM固件&#xff09;…...

【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具04

SQLSERVER的ImpDp和ExpDp工具演示 1、指定某些表作为导出对象外 (-exclude_table) 验证用&#xff1a;导出的表&#xff0c;导入到新的数据库 2、指定某些表作为导出对象外 (-exclude_table) 支持模糊检索&#xff0c;可以使用星号 以s开头的表作为导出对象外&#xff0c;…...

什么是中间件中间件有哪些

什么是中间件&#xff1f; 中间件&#xff08;Middleware&#xff09;是指在客户端和服务器之间的一层软件组件&#xff0c;用于处理请求和响应的过程。 中间件是指介于两个不同系统之间的软件组件&#xff0c;它可以在两个系统之间传递、处理、转换数据&#xff0c;以达到协…...

WebSocket connection failed 解决

WebSocket connection failed 解决 前言 这里如果是新手小白不知道 WebSocket 是什么的&#xff1f; 怎么使用的&#xff1f;或者想深入了解的 那可以 点击这里 几分钟带你快速了解并使用&#xff0c;已经一些进阶讲解&#xff1b; WebSocket&#xff0c;多应用于需要双向数据…...

C语言:取出32位数据的高十六位

目录 背景 目标 操作步骤 1. 右移 16 位 2. 掩码操作&#xff08;可选&#xff09; 代码实现 解释&#xff1a; 输出&#xff1a; 总结&#xff1a; 背景 假设我们有一个 32 位的无符号整数&#xff0c;通常它是由 4 个字节组成的。每个字节由 8 位构成&#xff0c;4…...

JUnit 5 条件测试注解详解

JUnit 5 条件测试注解详解 JUnit 5 提供了一系列条件测试注解&#xff0c;允许开发者根据运行时环境、配置或自定义逻辑动态决定是否执行测试。这些注解能有效减少误报&#xff0c;提升测试的灵活性和适应性。以下是所有条件测试注解的详细介绍及示例&#xff1a; 一、条件测试…...

1 Java 基础面试题(上)

文章目录 前言1. Java 中的序列化和反序列化是什么&#xff1f;1.1 序列化&#xff08;Serialization&#xff09;1.2 反序列化&#xff08;Deserialization&#xff09;1.3 serialVersionUID1.4 序列化的应用场景1.5 Transient 关键字 2. 为什么 Java 里面不支持多重继承&…...

个人笔记---关于详解threadlocal 上下文环境存储的最佳数据类型

个人原因很久没有写代码,对于一些基础的数据类型有一些忘记,可以根据gpt和我当时的问答进行复习 关于拦截器,由于在请求的到达controller处理器之前,拦截器(当然过滤器也可以实现,我感觉都差不多)就把上下文设置在了线程副本中,那么这个请求到处理器的这些代码进行查询出来的上…...

JVM监控和管理工具

基础故障处理工具 jps jps(JVM Process Status Tool)&#xff1a;Java虚拟机进程状态工具 功能 1&#xff1a;列出正在运行的虚拟机进程 2&#xff1a;显示虚拟机执行主类(main()方法所在的类) 3&#xff1a;显示进程ID(PID&#xff0c;Process Identifier) 命令格式 jps […...

【数据结构】树哈希

目录 一、树的同构1. 定义2. 具体理解(1) 结点对应(2) 孩子相同(3) 递归性质 3. 示例 二、树哈希1.定义2.哈希过程&#xff08;1&#xff09;叶节点哈希&#xff08;2&#xff09;非叶节点哈希&#xff08;3&#xff09;组合哈希值 3.性质&#xff08;1&#xff09; 唯一性 \re…...