突破编程_C++_C++11新特性(智能指针shared_ptr与weak_ptr)

1 std::shared_ptr 的概述

std::shared_ptr 是 C++11 标准库中引入的一种智能指针,用于表示共享所有权的智能指针。它允许多个 shared_ptr 实例共享同一个对象的所有权,并在最后一个引用该对象的 shared_ptr 被销毁或被重置时自动删除该对象。这种特性使得 std::shared_ptr 在需要在多个所有者之间共享对象时非常有用。

1.1 std::shared_ptr 的主要特性

(1)引用计数: std::shared_ptr 内部使用一个引用计数器来跟踪有多少个 shared_ptr 实例指向同一个对象。每次创建一个新的 shared_ptr 指向同一个对象时,引用计数就会增加;每次销毁或重置一个 shared_ptr 时,引用计数就会减少。

(2)自动删除: 当引用计数减少到 0 时,std::shared_ptr 会自动删除它所指向的对象,并释放相关资源。这避免了手动管理内存时可能出现的内存泄漏问题。

(3)自定义删除器: 与 std::unique_ptr 类似,std::shared_ptr 也支持自定义删除器。你可以提供一个可调用对象作为删除器,用于在引用计数为 0 时执行特定的清理逻辑。

(4)线程安全: std::shared_ptr 的引用计数操作是线程安全的,可以在多线程环境中安全地使用。

1.2 std::shared_ptr 的原理

以下是 std::shared_ptr 的详细原理:

(1)引用计数:

每个 std::shared_ptr 实例都关联一个引用计数,用于跟踪当前有多少个 shared_ptr 实例指向同一个对象。这个引用计数通常存储在一个控制块(control block)中,该控制块与所管理的对象一起在堆上分配。控制块可能还包含其他信息,比如自定义的删除器。

(2)对象的创建与引用:

当创建一个新的 std::shared_ptr 并让它指向一个对象时,会发生以下事情:

如果该对象是新分配的(例如,通过 std::make_shared 或 new 操作符),则会在堆上分配一个控制块,并将对象的指针和初始引用计数(通常为 1)存储在控制块中。
shared_ptr 内部保存指向这个控制块的指针,而不是直接保存对象的指针。通过控制块,shared_ptr 可以访问对象的指针和引用计数。
当另一个 shared_ptr 被构造为指向同一个对象时(例如,通过拷贝或赋值操作),引用计数会增加。这是因为新的 shared_ptr 实例也会保存指向同一个控制块的指针。

(3)对象的销毁与引用计数减少:

当 std::shared_ptr 被销毁(例如,离开作用域)或被重置为指向另一个对象时,会发生以下事情:

  • shared_ptr 所指向的控制块中的引用计数会减少。
  • 如果引用计数减少到 0,这意味着没有任何 shared_ptr 再指向该对象,此时控制块会调用相应的删除器(默认为 delete 操作符)来销毁对象,并释放对象和控制块所占用的内存。

(4)线程安全:

std::shared_ptr 的引用计数操作是线程安全的,这意味着在多线程环境中,多个线程可以同时修改同一个 shared_ptr 的引用计数,而不会导致数据竞争或未定义行为。这是通过使用原子操作或其他同步机制来实现的。

(5)自定义删除器:

除了默认的 delete 操作符外,std::shared_ptr 还允许使用自定义的删除器。自定义删除器是一个可调用对象(如函数、函数对象或 lambda 表达式),它会在引用计数减少到 0 时被调用,用于执行特定的清理逻辑。这使得 std::shared_ptr 能够更灵活地管理资源,例如关闭文件句柄、释放网络连接等。

通过引用计数和自动内存管理机制,std::shared_ptr 简化了动态分配对象的生命周期管理,减少了内存泄漏的风险,并提高了代码的可读性和可维护性。

1.3 注意事项

(1)避免循环引用: 在使用 std::shared_ptr 时,要特别注意避免循环引用,即两个或多个 shared_ptr 相互引用,形成一个无法被打破的引用环。这会导致引用计数永远无法减少到 0,从而造成内存泄漏。为了避免循环引用,可以使用 std::weak_ptr 来打破引用环。

(2)与原始指针的交互: 虽然 std::shared_ptr 提供了自动内存管理的便利,但在某些情况下,你可能仍然需要与原始指针进行交互。在这种情况下,要特别小心,确保不会意外地删除由 shared_ptr 管理的对象,或者创建额外的引用计数问题。

(2)性能考虑: 由于 std::shared_ptr 需要维护引用计数,相比裸指针或 std::unique_ptr,它可能会有一些额外的性能开销。因此,在不需要共享所有权的场景中,使用 std::unique_ptr 可能是更好的选择。

2 std::shared_ptr 的创建与初始化

2.1 使用 std::make_shared 创建

std::make_shared 是创建 std::shared_ptr 的推荐方式,因为它在单个内存分配中同时分配控制块和对象本身,从而提高了性能。

// 创建一个指向 int 的 shared_ptr,并初始化为 12  
std::shared_ptr<int> ptr1 = std::make_shared<int>(12);  // 创建一个指向自定义类型的 shared_ptr,并调用构造函数  
struct MyStruct {  MyStruct(int x, double y) : value1(x), value2(y) {}  int value1;  double value2;  
};  
std::shared_ptr<MyStruct> ptr2 = std::make_shared<MyStruct>(12, 3.14);

2.2 使用构造函数创建

虽然可以使用 shared_ptr 的构造函数直接创建智能指针,但这种方式不如 std::make_shared 高效,因为它需要两次内存分配:一次用于对象,一次用于控制块。

// 使用原始指针创建 shared_ptr(不推荐,可能导致内存泄漏)  
int* raw_ptr = new int(12);  
std::shared_ptr<int> ptr1(raw_ptr); // 注意:raw_ptr 不应再被 delete  // 使用空指针初始化 shared_ptr  
std::shared_ptr<int> ptr2; // 默认构造的 shared_ptr 不指向任何对象  // 复制另一个 shared_ptr 来初始化新的 shared_ptr(引用计数会增加)  
std::shared_ptr<int> ptr3 = ptr1; // ptr3 和 ptr1 现在共享同一个对象的所有权

2.3 使用 std::allocate_shared 创建

对于需要自定义内存分配的场景,可以使用 std::allocate_shared。它接受一个分配器作为参数,并使用该分配器来分配对象和控制块。

std::allocator<int> allocator;  
std::shared_ptr<int> ptr = std::allocate_shared<int>(allocator, 12);

2.4 自定义删除器

std::shared_ptr 还允许在构造时提供一个自定义的删除器。删除器是一个可调用对象,当最后一个 shared_ptr 不再指向对象时,它会被调用。

// 自定义删除器函数  
void customDelete(int* ptr) {  // 执行一些清理逻辑...  delete ptr;  
}  // 使用自定义删除器创建 shared_ptr  
std::shared_ptr<int> ptr1(new int(12), customDelete);  // 使用 lambda 表达式作为删除器  
std::shared_ptr<int> ptr2(new int(12), [](int* ptr) {  // 执行清理逻辑...  delete ptr;  
});

注意:当使用原始指针和自定义删除器创建 std::shared_ptr 时,必须确保原始指针只被一个 shared_ptr 管理,以避免内存泄漏。如果原始指针被多个 shared_ptr 管理,则当其中一个 shared_ptr 被销毁时,对象可能会被意外删除。因此,通常推荐使用 std::make_shared 来创建 std::shared_ptr。

2.5 初始化列表

在类的初始化列表中,也可以初始化 std::shared_ptr 成员变量。

class MyClass {  
public:  MyClass() : mySharedPtr(std::make_shared<int>(10)) {}  
private:  std::shared_ptr<int> mySharedPtr;  
};

3 std::shared_ptr 的自定义删除器

std::shared_ptr 的自定义删除器是一种功能强大的特性,它允许程序员为 shared_ptr 指定一个特定的函数或函数对象,该函数或对象会在最后一个 shared_ptr 失去对对象的所有权时(即引用计数减少到 0 时)被调用,以执行特定的清理逻辑。

自定义删除器通常用于处理那些不是通过 new 操作符分配的对象,或者那些需要特殊处理来释放的对象。例如,当你管理一些不是简单内存块的资源,如文件句柄、网络连接或者自定义内存管理器分配的内存时,你可能需要使用自定义删除器。

3.1 函数指针作为删除器

可以将一个普通的函数指针作为删除器传递给 shared_ptr 的构造函数。这个函数应该接受一个指向对象的指针,并执行适当的清理操作。

struct MyType { };void customDelete(MyType* ptr) {  // 清理逻辑,比如关闭文件句柄、释放网络连接等  delete ptr; // 假设 MyType 是通过 new 分配的  
}  std::shared_ptr<MyType> ptr(new MyType, customDelete);

3.2 函数对象(仿函数)作为删除器

也可以使用函数对象(也称为仿函数)作为删除器。函数对象是一个重载了 operator() 的类,它可以像普通函数一样被调用。

struct MyType { };struct CustomDeleter {  void operator()(MyType* ptr) const {  // 清理逻辑  delete ptr;  }  
};  std::shared_ptr<MyType> ptr(new MyType, CustomDeleter());

3.3 Lambda 表达式作为删除器

C++11 引入了 lambda 表达式,它们也可以作为 shared_ptr 的删除器。Lambda 表达式提供了一种简洁的方式来定义匿名函数对象。

struct MyType { };auto deleter = [](MyType* ptr) {  // 清理逻辑  delete ptr;  
};  std::shared_ptr<MyType> ptr1(new MyType, deleter);// 也可以直接使用
std::shared_ptr<MyType> ptr2(new MyType, [](MyType* ptr) {// 清理逻辑  delete ptr;
});

3.4 注意事项

(1)确保正确删除: 自定义删除器必须确保正确地释放资源。如果资源是通过 new 分配的,那么删除器应该使用 delete 来释放它。如果资源是通过其他方式(如自定义内存管理器)分配的,那么删除器应该使用相应的方法来释放它。

(2)避免重复删除: 如果原始指针被多个 shared_ptr 管理,并且每个 shared_ptr 都使用了自定义删除器,那么可能会导致资源被重复释放。因此,必须确保每个原始指针只被一个 shared_ptr 管理,或者确保删除器能够安全地处理重复调用。

(3)使用 std::make_shared: 通常推荐使用 std::make_shared 来创建 shared_ptr,因为它在一个内存分配中同时分配对象和控制块,从而提高了性能。当使用自定义删除器时,你可能需要直接使用 shared_ptr 的构造函数,但请确保正确管理内存。

(4)类型安全: 自定义删除器应该与 shared_ptr 所指向的对象类型相匹配。如果删除器期望一个不同类型的指针,那么可能会导致未定义行为。

4 std::shared_ptr 与数组

std::shared_ptr 是 C++11 引入的一种智能指针,用于共享对象的所有权。虽然 std::shared_ptr 主要用于单个对象的内存管理,但它也可以用于管理动态数组。不过,在使用 std::shared_ptr 管理数组时,需要特别注意一些细节,以确保正确和安全的内存管理。

4.1 使用 std::shared_ptr 的一个特化版本(推荐)

当使用 std::shared_ptr 管理数组时,需要使用特殊的构造函数,这些版本接受一个额外的参数来表示数组的大小。此外,当 std::shared_ptr 销毁其管理的数组时,它会调用 delete[] 而不是 delete。

#include <memory>  
#include <iostream>  struct MyStruct {  ~MyStruct() {  std::cout << "MyStruct destroyed\n";  }  
};  int main() 
{  // 使用 std::shared_ptr 管理数组  std::shared_ptr<MyStruct[]> arr_ptr(new MyStruct[5]);  // 当 arr_ptr 离开作用域时,MyStruct 数组会被自动使用 delete[] 删除  return 0;  
}

上面代码的输出为:

MyStruct destroyed
MyStruct destroyed
MyStruct destroyed
MyStruct destroyed
MyStruct destroyed

上面的这个例子创建了一个 std::shared_ptr<MyStruct[]> 来管理一个 MyStruct 类型的数组。注意类型中的方括号 [],它告诉 std::shared_ptr 正在管理一个数组,而不是单个对象。

4.2 自定义删除器与数组

就像普通的 std::shared_ptr 一样,也可以为数组版本的 std::shared_ptr 提供自定义删除器。这在某些情况下可能很有用,比如需要使用自定义的内存管理策略时。

void customArrayDelete(MyStruct* ptr) {// 自定义的数组删除逻辑  delete[] ptr;
}std::shared_ptr<MyStruct[]> arr_ptr(new MyStruct[5], customArrayDelete);

在这个例子中,传递了一个自定义的删除器 customArrayDelete 给 std::shared_ptr。当 arr_ptr 被销毁时,它会调用 customArrayDelete 来释放数组。

4.3 注意事项

  • 不要混用 new 和 delete[] 或 new[] 和 delete:当使用 std::shared_ptr 管理数组时,确保你使用 new[] 来分配数组,这样 std::shared_ptr 就可以在销毁时正确地使用 delete[] 来释放内存。如果错误地使用了 new(而不是 new[]),那么当 std::shared_ptr 尝试使用 delete[] 来释放内存时,会导致未定义行为。
  • 不要使用 std::make_shared 管理数组:虽然 std::make_shared 通常是创建 std::shared_ptr 的推荐方式,但它并不直接支持数组。尝试使用 std::make_shared 来创建管理数组的 std::shared_ptr 会导致编译错误,因为 std::make_shared 的模板参数推导不会正确处理数组类型。

5 std::shared_ptr 的循环引用问题

std::shared_ptr 的循环引用问题是在使用智能指针时经常遇到的一个难题。循环引用指的是两个或多个 std::shared_ptr 对象相互引用,形成一个闭环,导致它们的引用计数永远无法降至零,从而无法自动释放所管理的内存资源。

(1)循环引用的产生

循环引用通常发生在对象间存在相互依赖关系时。例如,考虑两个类 A 和 B,每个类都持有一个指向对方类型的 std::shared_ptr。

class B;  class A {  
public:  std::shared_ptr<B> b_ptr;  // ... 其他成员 ...  
};  class B {  
public:  std::shared_ptr<A> a_ptr;  // ... 其他成员 ...  
};  void create_cycle() {  auto a = std::make_shared<A>();  auto b = std::make_shared<B>();  a->b_ptr = b;  b->a_ptr = a;  // 此时,a 和 b 形成了循环引用  
}

在 create_cycle 函数中,创建了两个 std::shared_ptr 对象 a 和 b,并将它们相互引用。这意味着 a 的引用计数因为 b->a_ptr 的存在而增加,同时 b 的引用计数因为 a->b_ptr 的存在而增加。当 create_cycle 函数结束时,a 和 b 离开作用域,但它们的引用计数仍然不为零,因此它们所管理的内存不会被释放。

(2)循环引用的影响

循环引用会导致内存泄漏,因为 std::shared_ptr 无法检测到循环引用并自动释放内存。即使所有外部对 a 和 b 的引用都已消失,它们所指向的对象仍然不会被销毁,因为它们的引用计数永远不会降到零。

(3)解决循环引用
有几种方法可以解决 std::shared_ptr 的循环引用问题:

使用弱指针 (std::weak_ptr):
弱指针不控制所指向对象的生命周期,它们只是观察 std::shared_ptr 的对象。将循环引用中的一个 std::shared_ptr 替换为 std::weak_ptr 可以打破循环。当只有弱指针指向一个对象时,该对象可以被安全地删除。
在下面的章节会详细讲解该方法。

手动管理内存:
在某些情况下,可能需要回退到使用原始指针和手动管理内存。这通常不是首选方法,因为它容易引入内存泄漏和野指针等问题,但它可以作为一种解决方案,特别是在其他方法不适用或过于复杂时。

使用其他智能指针:
根据具体情况,可以考虑使用其他类型的智能指针,如 std::unique_ptr 或 std::observer_ptr(C++20 中引入),这些智能指针具有不同的所有权语义,可能更适合某些情况。

解决循环引用问题的关键是确保没有路径使得对象的引用计数永远无法降至零。通过仔细设计类的关系和内存管理策略,可以有效地避免循环引用导致的内存泄漏问题。

6 std::weak_ptr 的引入与使用

std::weak_ptr 是 C++11 引入的一种智能指针,主要用于解决 std::shared_ptr 之间的循环引用问题,以避免内存泄漏。与 std::shared_ptr 不同,std::weak_ptr 不控制所指向对象的生命周期,它只是对对象的一个弱引用,不会增加对象的引用计数。

(1)引入

在 C++ 中,智能指针被设计用来自动管理动态分配的内存,以防止内存泄漏。std::shared_ptr 允许多个智能指针共享同一个对象的所有权,当最后一个 std::shared_ptr 被销毁或重置时,它所指向的对象才会被删除。然而,当两个或多个 std::shared_ptr 对象相互引用,形成一个闭环时,它们的引用计数将永远无法降至零,导致内存无法被释放,这就是循环引用问题。

为了解决这个问题,C++11 引入了 std::weak_ptr。它允许程序员创建对 std::shared_ptr 所管理对象的弱引用,但不会增加对象的引用计数。当最后一个 std::shared_ptr 销毁时,即使还有 std::weak_ptr 引用该对象,对象也会被删除。同时,std::weak_ptr 提供了检查其引用的 std::shared_ptr 是否仍然有效的机制,从而避免了悬空指针的问题。

(2)使用

使用 std::weak_ptr 的主要场景是在需要观察 std::shared_ptr 所管理的对象,但又不希望控制其生命周期时。例如,在解决循环引用问题时,可以将其中一个 std::shared_ptr 替换为 std::weak_ptr。

下面是一个使用 std::weak_ptr 解决循环引用的示例:

#include <memory>  
#include <iostream>  class B;class A {
public:~A() {std::cout << "A destroyed\n";}
public:std::weak_ptr<B> b_ptr; // 使用 weak_ptr 替代 shared_ptr 以解决循环引用问题  
};class B {
public:~B() {std::cout << "B destroyed\n";}
public:std::shared_ptr<A> a_ptr;
};int main()
{{auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;}// 此时,a 和 b 之间存在循环引用,但由于 a 使用的是 weak_ptr,  // 因此当离开作用域时,b 会首先被销毁,随后 a 也会被销毁,避免了内存泄漏。  // 输出 "B destroyed" 和 "A destroyed" return 0;
}

在这个例子中,A 类使用 std::weak_ptr 来引用 B 类的对象,而 B 类使用 std::shared_ptr 来引用 A 类的对象。这样,当 create_objects 函数结束时,即使 a 和 b 之间存在循环引用,由于 a 使用的是弱引用,因此当 b 的 std::shared_ptr 被销毁时,B 的对象会被删除,随后 A 的对象也会因为没有任何 std::shared_ptr 引用它而被删除,从而避免了内存泄漏。

需要注意的是,std::weak_ptr 没有重载 * 和 -> 等运算符,因此不能单独用来访问对象。如果需要访问 std::weak_ptr 所引用的对象,需要首先将其转换为 std::shared_ptr,这可以通过调用 std::weak_ptr 的 lock 方法来实现。如果转换成功(即 std::weak_ptr 仍然有效),lock 方法将返回一个指向对象的 std::shared_ptr;否则,将返回一个空的 std::shared_ptr。

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

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

相关文章

Redis:使用redis-dump导出、导入、还原数据实例

redis的备份和还原&#xff0c;借助了第三方的工具&#xff0c;redis-dump 1、安装必要环境 yum -y install zlib-devel openssl-devel2、安装redis-dump 安装ruby&#xff1a; ruby下载地址&#xff1a;https://www.ruby-lang.org/zh_cn/downloads/ 我下载的是 2.5.0 版本…

2024(最新)前端面試題----歡迎補充

HTML、css相關。 HTML5新特性 1、增强了表单&#xff0c;input新增了一些type&#xff1a;color----定义调色板tel-----定义包含电话号码的输入域email—定义包含email地址的输入域search–定义2、搜索域number–定义包含数值的输入域date----定义选取日、月、年的输入域 3、Vi…

Elasticsearch(11) intervals的使用

elasticsearch version 7.10.1 在Elasticsearch中&#xff0c;intervals查询是用来做复杂的区间表达式匹配的&#xff0c;它可以基于分析过的文本字段执行一系列复杂的关系运算。intervals查询特别适合于那些需要对文本数据进行模式匹配&#xff0c;而不只是单一词汇匹配的情况…

动态规划课堂6-----回文串问题

目录 引言&#xff1a; 例题1&#xff1a;回文子串 例题2&#xff1a;回文串分割IV 例题3&#xff1a;分割回文串II 例题4&#xff1a;最长回文子序列 例题5&#xff1a;让字符串成为回文串的最小插入次数 引言&#xff1a; 回文字符串 是正着读和倒过来读一样的字符串。…

【周赛】第385场周赛

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 【1】100212.统计前后缀下标对 100212. 统计前后缀下标对 Ihttps://leetcode.cn/problems/count-prefix-and-suffix-pairs-i/ 熟…

4.1_4 文件的物理结构

文章目录 4.1_4 文件的物理结构&#xff08;一&#xff09;文件块、磁盘块&#xff08;二&#xff09;文件分配方式——连续分配&#xff08;三&#xff09;文件分配方式——链接分配&#xff08;1&#xff09;链接分配——隐式链接&#xff08;2&#xff09;链接分配——显式链…

OpenStack之存储cinder

一、 存储 1、DAS 1)存储更多的依赖服务器主机操作系统进行数据的IO读写和存储维护管理&#xff0c;数据备份和恢复要求占用服务器主机资源&#xff08;包括CPU、系统IO等&#xff09; 2)直连式存储与服务器主机之间的连接通道通常采用SCSI连接&#xff0c;随着服务器CPU的处…

Type-C接口介绍

1、USB介绍 &#xff08;1&#xff09;标准USB A型连接器&#xff08;左&#xff09;及B型连接器&#xff08;右&#xff09; 引脚1 VCC&#xff08;5V&#xff09; 引脚2 Data- 引脚3 Data 引脚4 接地 &#xff08;2&#xff09;Micro USB 引脚定义及OTG (USB-HOST) …

QT 多线程使用以及注意事项

Qt 是一个跨平台的 C 应用程序开发框架&#xff0c;提供了丰富的库和工具&#xff0c;用于开发图形用户界面、网络应用、数据库应用等。Qt 中的多线程支持使得开发者可以方便地在应用程序中实现并发执行的功能。 以下是一个简单的使用 Qt 多线程的代码例程&#xff1a; #inclu…

单片机实践:开发板上运行AES128防盗算法

本章内容主要是在开发板上运行AES128防盗算法&#xff0c;并且通过串口调试助手观测防盗算法加密后的值以及解密后的值&#xff0c;判断防盗算法是否在开发板上正确运行。 1&#xff0c;防盗算法 以下为AES128防盗算法源码&#xff1a; aes.h文件源码 #ifndef _AES_H_ #def…

mvcc介绍

前提&#xff1a;在介绍mvcc之前&#xff0c;先简单介绍一下mysql事务的相关问题&#xff0c;mvcc归根结底是用来解决事务并发问题的&#xff0c;当然这个解决不是全部解决&#xff0c;只是解决了其中的一部分问题&#xff01; mysql事务 一、事务的基本要素&#xff08;ACID&a…

k8s CKA upgrade - Kubeadm 版本升级实测

升级版本最好是逐步去升级&#xff0c;不要跨越多个大版本&#xff0c;可能会出错 大体流程&#xff1a; 1.先确定升级版本 2.升级kubeadm 3.驱逐节点 4.升级kubelet和kubectl 5.重启kubelet服务 6.恢复节点&#xff0c;使其上线 1.查看现版本&#xff1a;升级版本 kubectl ge…

python从入门到精通(十五):python爬虫完整学习大纲

一、基础知识 爬虫的基本概念和工作原理。 HTTP 协议和网页结构。 Python 爬虫开发的基础库&#xff0c;如 requests、BeautifulSoup 等。 常见的反爬虫机制和应对方法。 二、爬虫逆向的技术 代理服务器和 IP 封锁突破。 用户代理和请求头模拟。 JavaScript 解析和执行。 验证码…

华为配置Hotspot2.0无线网络示例

配置Hotspot2.0无线网络示例 组网图形 图1 配置Hotspot2.0无线网络组网图 组网需求配置思路配置注意事项操作步骤配置文件 组网需求 某网络服务商在原有移动网络业务的基础上&#xff0c;新增部署WLAN网络接入业务&#xff0c;为用户提供更好的网络体验。但传统的WLAN网络业…

React/RN组件避免重复渲染的一些技巧

组件基础 回顾下React中创建组件的几种方式&#xff1a; ES5的React.createClass方式ES6的React.Component方式无状态的函数组件方式带hooks的函数组件方式 这里我们只讨论Component和函数组件。我们知道Component是否发生重渲染是由shouldComponentUpdate决定的&#xff0c;…

【每日一题】2024年3月汇编(上)

3.1【2369】检查数组是否存在有效划分 2369. 检查数组是否存在有效划分https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/ 1.这样的判断可以用动态规划来解决&#xff0c;用一个长度为(n1) 的数组来记录 是否存在有效划分&#xff0c;dp[i]…

乘法-逆矩阵

文章目录 1. 矩阵相乘-4种方式1.1 CAB1.2 AX 列组合1.3 XB 行组合 2. A,AB, A T A^T AT的逆3. 高斯消元法求 A − 1 A^{-1} A−1 1. 矩阵相乘-4种方式 1.1 CAB 假设我们要求得矩阵CAB &#xff0c;可以用如下公式表示 c i j ∑ k 1 N a i k b k j (1) c_{ij}\sum_{k1}^Na_…

Bean的作用域、Bean的自动装配、注解自动装配 (Spring学习笔记五)

1、Bean 的作用域 官网上显示有六种 1、Bean的作用域默认的是singleton&#xff08;单例模式的实现&#xff09; 也可以显示的设置&#xff08;单例模式的实现&#xff09; <!--用scope可以设置Bean的作用域--><bean id"user2" class"com.li.pojo.Us…

如何实现分词

分词&#xff08;Tokenization&#xff09;是自然语言处理&#xff08;NLP&#xff09;中的一个基本步骤&#xff0c;特别是在构建搜索引擎时&#xff0c;它将文本拆分成单词、短语或其他有意义的元素&#xff08;称为“词素”或“tokens”&#xff09;。对于中文文本来说&…

A*(AStar)算法总结

简介 A* 算法&#xff08;念做&#xff1a;A Star&#xff09;是一种常用的路径查找和图形遍历算法&#xff0c;具有较好的性能和准确度。让我为您简要介绍一下 A* 算法的原理和实现。 广度优先搜索&#xff1a; 广度优先搜索以广度作为优先级进行搜索。从起点开始&#xff0…