深入理解Qt智能指针

目录

1.引言

2.共享数据

2.1.特点

2.2.QSharedData 

2.3.隐式共享

2.4.显示共享

3.共享指针

3.1.QSharedPointer

3.2.QWeakPointer

 4.范围指针

4.1.QScopedPointer

4.2.QScopedArrayPointer

5.追踪特定QObject对象生命

6.总结


1.引言

        在 Qt 中,智能指针是一种能够自动管理对象生命周期的指针类型。通过使用智能指针,可以避免手动释放内存和处理悬挂指针等常见的内存管理问题。根据不同的使用场景, 可分为以下几种:

1) 共享数据(QSharedData). 隐式或显式的共享数据(不共享指针), 也被称为 侵入式指针.

        QSharedDataPointer : 指向隐式共享对象的指针.

        QExplicitlySharedDataPointer : 指向显式共享对象的指针.

2) 共享指针. 线程安全.

        QSharedPointer: 有点像 std::shared_ptr, boost::shared_ptr. 维护引用计数, 使用上最像原生指针.

        QWeakPointer: 类似于boost::weak_ptr. 作为 QSharedPointer 的助手使用. 未重载*和->. 用于解决强引用形成的相互引用.

3) 范围指针. 为了RAII目的, 维护指针所有权, 并保证其在超出作用域后恰当的被销毁, 非共享.

        QScopedPointer: 相当于 std::unique_ptr,  所有权唯一, 其拷贝和赋值操作均为私有. 无法用于容器中.

        QScopedArrayPointer

4) 追踪给定 QObject 对象生命, 并在其析构时自动设置为 NULL.

        QPointer

2.共享数据

2.1.特点

1)共享数据是为了实现 “读时共享, 写时复制”. 其本质上是延迟了 执行深拷贝 的时机到了需要修改其值的时候.

C++之写时复制(CopyOnWrite)

2)C++实现为在拷贝构造和赋值运算符函数中不直接深度拷贝, 而是维护一个引用计数并获得一个引用或指针. 在需要改变值的方法中再执行深度拷贝.

3)隐式共享为, 我们无需管理深度拷贝的时机, 它会自动执行.

4)显式共享为, 我们需要人为判断什么时候需要深度拷贝, 并手动执行拷贝.

5)QSharedData 作为共享数据对象的基类. 其在内部提供 线程安全的引用计数.

6)其与 QSharedDataPointer 和 QExplicitlySharedDataPointer 一起使用.

7)以上三个类都是可重入的.

2.2.QSharedData 

        QSharedData是Qt框架中的一个基类,用于实现隐式共享(implicit sharing)机制。隐式共享是一种优化技术,允许多个对象共享同一份数据,直到数据被某个对象修改时,才会复制数据并分配给修改者,从而实现高效的内存使用和减少不必要的数据复制。下面是它的定义和实现:

(Qt5.12.12版本,源码路径:.\Qt\Qt5.12.12\5.12.12\Src\qtbase\src\corelib\tools\qshareddata.h)

class Q_CORE_EXPORT QSharedData
{
public:mutable QAtomicInt ref;inline QSharedData() : ref(0) { }inline QSharedData(const QSharedData &) : ref(0) { }private:// using the assignment operator would lead to corruption in the ref-countingQSharedData &operator=(const QSharedData &);
};

QAtomicInt 是 Qt实现的原子变量,是线程安全的。

从代码可以总结出QSharedData的主要特点:

1)引用计数:QSharedData内部维护了一个引用计数(通常是一个QAtomicInt类型的成员变量),用于跟踪当前有多少对象正在共享这份数据。当对象被复制或赋值时,引用计数会增加;当对象被销毁或指向新的数据时,引用计数会减少。只有当引用计数为零时,共享的数据才会被销毁。

2)线程安全:QSharedData的引用计数是线程安全的,这意味着它可以在多线程环境中安全地使用,而无需额外的同步措施。

3)数据封装:通过继承QSharedData,开发者可以将需要共享的数据封装在派生类中,并通过QSharedDataPointer来管理这些数据的访问和共享。

2.3.隐式共享

QSharedDataPointer:表示指向隐式共享对象的指针;其在写操作时, 会自动调用detach(). 该函数在当共享数据对象引用计数大于1, 会执行深拷贝, 并将该指针指向新拷贝内容. (这是在该类的非 const 成员函数中自动调用的, 我们使用时不需要关心),这一点也可以从它的源码中体现出来,如下所示:

QSharedDataPointer 每次拷贝构造和赋值构造都是浅拷贝,只复制 QSharedData 指针的值,并增加引用计数:

(Qt5.12.12版本,源码路径:.\Qt\Qt5.12.12\5.12.12\Src\qtbase\src\corelib\tools\qshareddata.h)

 inline QSharedDataPointer(const QSharedDataPointer<T> &o) : d(o.d) { if (d) d->ref.ref(); }inline QSharedDataPointer<T> & operator=(const QSharedDataPointer<T> &o) {if (o.d != d) {if (o.d)o.d->ref.ref();T *old = d;d = o.d;if (old && !old->ref.deref())delete old;}return *this;}inline QSharedDataPointer &operator=(T *o) {if (o != d) {if (o)o->ref.ref();T *old = d;d = o;if (old && !old->ref.deref())delete old;}return *this;}

而写时深拷贝主要是借助接口的 const 声明来判断的,如果是非 const 接口,那么就会进行深拷贝,并修改引用计数:

template <class T> class QSharedDataPointer
{
public://... ...inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }inline T &operator*() { detach(); return *d; }inline const T &operator*() const { return *d; }inline T *operator->() { detach(); return d; }inline const T *operator->() const { return d; }inline operator T *() { detach(); return d; }inline operator const T *() const { return d; }inline T *data() { detach(); return d; }inline const T *data() const { return d; }inline const T *constData() const { return d; }void QSharedDataPointer<T>::detach_helper(){T *x = clone();x->ref.ref();if (!d->ref.deref())delete d;d = x;}template <class T>T *QSharedDataPointer<T>::clone(){return new T(*d);}
};

以下是一个简单的示例,展示了如何使用QSharedData和QSharedDataPointer来实现隐式共享:

#include <QSharedData>  
#include <QSharedDataPointer>  
#include <QDebug>  // 定义共享数据类,继承自QSharedData  
class MySharedData : public QSharedData {  
public:  MySharedData() : value(0) {}  MySharedData(int val) : value(val) {}  int value;  
};  // 定义使用隐式共享的类  
class MyClass {  
public:  MyClass() : d(new MySharedData) {}  MyClass(const MyClass &other) : d(other.d) {}  int getValue() const { return d->value; }  void setValue(int val) {  // 当修改数据时,可能需要分离(detach)以创建新的数据副本  // 这里为了简化,我们直接修改值,但在实际使用中可能需要更复杂的逻辑  d->value = val;  }  private:  QSharedDataPointer<MySharedData> d;  
};  int main() {  MyClass obj1(10);  MyClass obj2 = obj1; // obj1和obj2现在共享相同的数据  qDebug() << obj1.getValue() << obj2.getValue(); // 输出: 10 10  obj2.setValue(20); // 修改obj2的值,但这里我们假设没有分离,实际上应该处理分离逻辑  qDebug() << obj1.getValue() << obj2.getValue(); // 假设未分离,输出仍应为: 10 20(但实际上可能是未定义行为)  return 0;  
}  // 注意:上面的示例中,我们没有处理分离逻辑,因为在实际使用中,当需要修改数据时,  
// QSharedDataPointer会提供detach()方法来创建一个新的数据副本,以避免影响其他共享者。  
// 但为了保持示例的简洁性,这里省略了这部分逻辑。

请注意,上面的示例为了简化而省略了分离逻辑。在实际使用中,当需要修改数据时,应该调用QSharedDataPointer的detach()方法来确保修改不会影响其他共享者。Qt的隐式共享机制正是通过这种方式来实现高效的数据共享和修改的。

2.4.显示共享

  QExplicitlySharedDataPointer :表示指向显式共享对象的指针,与QSharedDataPointer不同的地方在于, 它不会在非const成员函数执行写操作时, 自动调用 detach(),所以需要我们在写操作时, 手动调用 detach(),它的行为很像 C++ 常规指针, 不过比指针好的地方在于, 它也维护一套引用计数, 当引用计数为0时会自动设置为 NULL. 避免了悬空指针的危害.

        这些特点可以从它的源码体现出来:

(Qt5.12.12版本,源码路径:.\Qt\Qt5.12.12\5.12.12\Src\qtbase\src\corelib\tools\qshareddata.h)

template <class T> class QExplicitlySharedDataPointer
{
public:typedef T Type;typedef T *pointer;inline T &operator*() const { return *d; }inline T *operator->() { return d; }inline T *operator->() const { return d; }inline T *data() const { return d; }inline const T *constData() const { return d; }inline T *take() { T *x = d; d = nullptr; return x; }inline void detach() { if (d && d->ref.load() != 1) detach_helper(); }inline void reset(){if(d && !d->ref.deref())delete d;d = nullptr;}inline operator bool () const { return d != nullptr; }。。。template <class T>void detach_helper(){T *x = clone();x->ref.ref();if (!d->ref.deref())delete d;d = x;}private:T *d;
};

示例代码:

#include <QSharedData>  
#include <QExplicitlySharedDataPointer>  class MySharedData : public QSharedData {  
public:  int value;  MySharedData() : value(0) {}  // 允许对共享数据进行深度复制  MySharedData(const MySharedData &other)  : QSharedData(other), value(other.value) {}  // 如果需要,可以添加其他成员函数  
};  class MyClass {  
public:  QExplicitlySharedDataPointer<MySharedData> data;  MyClass() {  data = QSharedDataPointer<MySharedData>(new MySharedData());  }  // 其他成员函数,可以访问和修改 data 指向的数据  
};

注意事项

1)当通过 QExplicitlySharedDataPointer 访问和修改数据时,需要注意线程安全。在多线程环境中,可能需要使用互斥锁(如 QMutex)来保护数据。

2)隐式共享模式可能会增加代码的复杂性,因为你需要确保在适当的时候进行数据的深拷贝。

3)并非所有情况下都需要使用隐式共享。在数据很小或者数据几乎不会被多个对象共享的情况下,使用传统的值语义可能更为简单和高效。

3.共享指针

3.1.QSharedPointer

        QSharedPointer 是 Qt 提供的共享引用计数的智能指针,可用于管理动态分配的对象。它通过引用计数跟踪对象的引用次数,当引用计数归零时会自动删除对象。可以通过多个 QSharedPointer 共享同一个对象,对象只会在最后一个引用者释放它时才会被删除。

        它可用于容器中;可提供自定义 Deleter, 所以可用于 delete [] 的场景;线程安全. 多线程同时修改其对象无需加锁. 但其指向的内存不一定线程安全, 所以多线程同时修改其指向的数据, 还需要加锁.

        它类似于C++11中的std::shared_ptr

示例如下:

#include <QSharedPointer>  class MyClass {  
public:  MyClass() { /* 构造函数 */ }  ~MyClass() { /* 析构函数 */ }  // 其他成员函数  
};  int main() {  // 创建一个 QSharedPointer 实例,指向一个新分配的 MyClass 对象  QSharedPointer<MyClass> ptr1(new MyClass());  // 复制 ptr1,现在 ptr2 也指向同一个 MyClass 对象  QSharedPointer<MyClass> ptr2 = ptr1;  // ptr1 和 ptr2 的引用计数都是 1  // 当 ptr1 超出作用域并被销毁时,引用计数减少到 1,对象不会被删除  {  QSharedPointer<MyClass> ptr3 = ptr1;  // 现在 ptr1、ptr2 和 ptr3 的引用计数都是 2  }  // ptr3 超出作用域并被销毁,引用计数减少到 1  // 当 ptr2 也超出作用域并被销毁时,引用计数变为 0,MyClass 对象被删除  return 0;  
}

注意事项

1)循环引用:当两个或多个 QSharedPointer 实例相互指向对方时,会导致引用计数永远无法减少到 0,从而发生内存泄漏。为了解决这个问题,可以使用 QWeakPointer 来打破循环引用。

2)线程安全:虽然 QSharedPointer 的引用计数操作是线程安全的,但指向的对象本身的访问和修改可能需要额外的同步措施,以避免数据竞争。

3)性能考虑:虽然 QSharedPointer 提供了方便的内存管理功能,但引用计数的维护会增加一定的性能开销。在性能敏感的应用中,需要权衡这种开销与便利性之间的关系。

3.2.QWeakPointer

        它提供了对 QSharedPointer 所管理对象的弱引用功能。与 QSharedPointer 的强引用不同,QWeakPointer 不会增加对象的引用计数,因此不会影响对象的生命周期。这使得 QWeakPointer 成为QSharedPointer解决循环引用问题的有力工具。

示例如下:

#include <QSharedPointer>  
#include <QWeakPointer>  class MyClass {  
public:  MyClass() { /* 构造函数 */ }  ~MyClass() { /* 析构函数 */ }  // 其他成员函数  
};  int main() {  // 创建一个 QSharedPointer 实例,指向一个新分配的 MyClass 对象  QSharedPointer<MyClass> sharedPtr(new MyClass());  // 创建一个 QWeakPointer 实例,通过赋值操作指向 sharedPtr 所管理的对象  QWeakPointer<MyClass> weakPtr = sharedPtr;  // 现在 weakPtr 指向 sharedPtr 所管理的对象,但不会增加对象的引用计数  // ...  // 在某个时刻,sharedPtr 被销毁或超出作用域  // 此时,如果 weakPtr 尝试访问对象,它将检测到对象已被删除,并可以避免悬垂指针的问题  // 通过 lock() 方法,可以将 QWeakPointer 转换为 QSharedPointer(如果对象仍然存在)  if (!weakPtr.isNull()) {  QSharedPointer<MyClass> lockedPtr = weakPtr.lock();  // 现在可以使用 lockedPtr 安全地访问对象  }  return 0;  
}

注意事项

1) QWeakPointer 不能用于直接访问对象,因为它不保证对象在访问时仍然有效。在访问对象之前,应该使用 isNull() 方法检查 QWeakPointer 是否为空。

2) QWeakPointer 可以通过 lock() 方法转换为 QSharedPointer,但只有在对象仍然存在时才会成功。如果对象已被删除,lock() 方法将返回一个空的 QSharedPointer

3) 在多线程环境中使用 QWeakPointer 时,需要注意线程安全问题。虽然 QWeakPointer 的操作本身是线程安全的,但指向的对象本身的访问和修改可能需要额外的同步措施。

 4.范围指针

4.1.QScopedPointer

      QScopedPointer的主要目的是管理动态分配(在堆上创建)的对象的生命周期。当 QScopedPointer 被销毁时,它会自动删除它所指向的对象,从而避免了内存泄漏的风险。

        QScopedPointer 不支持复制操作,这避免了因复制导致的对象重复删除问题。

        它的功能类似std::unique_ptr。

示例如下:

MyClass *foo() {QScopedPointer<MyClass> myItem(new MyClass);// 一些逻辑if (some condition) {return nullptr; // myItem在这里会被删除}return myItem.take(); // 释放scoped指针并返回
}
// 在异常情况下,item也会被删除

在类成员变量中使用QScopedPointer可以避免编写析构函数:

class MyClass {
public:MyClass() : myPtr(new int) {}
private:QScopedPointer<int> myPtr; // 在包含对象删除时自动删除
}

4.2.QScopedArrayPointer

  QScopedArrayPointer 是 Qt 框架中的一个智能指针模板类,它专门用于管理动态分配的数组。与 QScopedPointer 类似,QScopedArrayPointer 保证了当它的作用域结束时,所指向的数组会被自动使用 delete[] 运算符删除,从而避免了内存泄漏的风险。

        与 QScopedPointer 一样,QScopedArrayPointer 也不支持复制操作,这避免了因复制导致的数组重复删除问题。

示例如下:

#include <QScopedArrayPointer>  void someFunction() {  QScopedArrayPointer<int> intArray(new int[10]);  // 初始化数组元素...  for (int i = 0; i < 10; ++i) {  intArray[i] = i * 2;  }  // ... 在这里使用 intArray 指向的数组 ...  // 当函数返回时,QScopedArrayPointer 会自动删除 intArray 指向的数组。  
}

5.追踪特定QObject对象生命

        QPointer专为 QObject 及其派生类对象设计。它的主要特点和用途如下:

主要特点

1)自动置空:当 QPointer 所指向的 QObject 对象被销毁时,QPointer 会自动被置为 nullptr,从而避免了悬挂指针(dangling pointer)的问题。这是通过 QObject 的析构机制实现的,当 QObject 对象被销毁时,它会通知所有指向它的 QPointer,使它们变为空指针。

2)空安全:由于 QPointer 会在对象销毁时自动置空,因此使用 QPointer 时无需担心指针悬挂的问题,增加了代码的安全性和稳定性。

3)对象限制:QPointer 只能指向 QObject 及其派生类的对象,这是因为它的工作原理依赖于 QObject 的析构机制。

使用场景

1)跨作用域对象引用:当需要在不同的作用域或类中引用同一个 QObject 对象时,使用 QPointer 可以确保即使原始对象被销毁,引用也不会导致程序崩溃。

2)信号与槽机制:在 Qt 的信号与槽机制中,如果槽函数可能引用已经销毁的对象,使用 QPointer 可以避免悬挂指针的问题。

3)对象生命周期管理:在某些复杂的场景中,需要手动管理对象的生命周期时,QPointer 可以提供一种相对安全的方式来引用这些对象。

示例代码:

#include <QPointer>  
#include <QLabel>  // 假设有一个 QLabel 对象  
QLabel *label = new QLabel("Hello, QPointer!");  // 创建一个 QPointer 来引用 QLabel 对象  
QPointer<QLabel> pLabel(label);  // ... 在这里可以安全地使用 pLabel ...  // 当 label 被 delete 后,pLabel 会自动变为 nullptr  
delete label;  // 此时 pLabel.isNull() 将返回 true  
if (pLabel.isNull()) {  // pLabel 已经是 nullptr,可以安全地处理  
}

注意事项

1)手动销毁对象:虽然 QPointer 会在对象销毁时自动置空,但销毁对象本身(即调用 delete)仍然需要手动进行。

2)不支持非 QObject 对象:由于 QPointer 的工作原理依赖于 QObject,因此它不能用于指向非 QObject 类的对象。

3)性能考虑:虽然 QPointer 提供了额外的安全性,但它也可能带来一些性能开销,因为需要维护额外的机制来跟踪对象的销毁。在性能敏感的应用中,需要权衡这种开销与安全性之间的平衡。

6.总结

        Qt 智能指针是 Qt 框架中非常重要的特性之一,它们通过自动管理内存和资源,大大简化了 Qt 应用程序的开发和维护工作。开发者应根据具体需求选择合适的智能指针类型,并遵循相关的使用注意事项,以编写出更安全、更稳定的 Qt 应用程序。

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

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

相关文章

计算样本之间的相似度

文章目录 前言一、距离度量1.1 欧几里得距离&#xff08;Euclidean Distance&#xff09;1.2 曼哈顿距离&#xff08;Manhattan Distance&#xff09;1.3 切比雪夫距离&#xff08;Chebyshev Distance&#xff09;1.4 闵可夫斯基距离&#xff08;Minkowski Distance&#xff09…

docker容器技术、k8s的原理和常见命令、用k8s部署应用步骤

容器技术 容器借鉴了集装箱的概念&#xff0c;集装箱解决了什么问题呢&#xff1f;无论形状各异的货物&#xff0c;都可以装入集装箱&#xff0c;集装箱与集装箱之间不会互相影响。由于集装箱是标准化的&#xff0c;就可以把集装箱整齐摆放起来&#xff0c;装在一艘大船把他们…

浏览器插件利器-allWebPluginV2.0.0.14-stable版发布

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX插件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持谷歌、火狐等浏…

Spring Boot+Blockchain:区块链入门Demo

1. 引言 区块链技术近年来迅速发展&#xff0c;其去中心化、不可篡改和透明性等特点吸引了众多开发者和企业的关注。为了便于理解和应用区块链技术&#xff0c;本文将介绍如何使用Spring Boot集成区块链&#xff0c;构建一个简单的区块链Demo。 2. 项目准备 2.1 环境要求 在…

MYSQL安装及环境配置

1.数据库下载 1.1 浏览器下载相应版本&#xff0c;如果相应版本不在此页&#xff0c;可点击Archives &#xff0c;然后选择相应版本 https://dev.mysql.com/downloads/mysql/ 1.2 放置指定目录&#xff0c;并将其解压 2.配置数据库环境变量 2.1 使用电脑win键 Q &#xff0c;…

在C++中使用的错误处理策略

在C中&#xff0c;错误处理是一个重要且复杂的主题&#xff0c;因为它要求开发者在设计和编码时考虑到程序可能遇到的各种异常情况。C提供了几种不同的机制来处理错误&#xff0c;每种机制都有其适用的场景和优缺点。下面我将概述几种常见的C错误处理策略&#xff1a; 1. 返回…

SQL的时间格式和文本灵活转换

日期的格式&#xff0c;在日常的数据分析中&#xff0c;常常使用 特别是在按照日、月、年进行汇总分析&#xff0c;使用起来&#xff0c;往往会有差异 如果格式比较复杂&#xff0c;可以考虑进行文本转化的处理 这里有比较推荐的函数&#xff1a; 1.CONVERT()函数 适用于SQL …

51单片机STC89C52RC——16.1 五项四线步进电机

目的/效果 让步进电机 正向转90度&#xff0c;逆向转90度 一&#xff0c;STC单片机模块 二&#xff0c;步进电机 2.2 什么是步进电机&#xff1f; 步进电机可以理解为&#xff1a;是一个按照固定步幅运动的“小型机器”。它与普通电机不同点在于&#xff0c;普通电机可以持…

CompletionService

必备知识&#xff1a; 三种创建线程的方式 java线程池 CompletionService是Java并发库中的一个接口&#xff0c;用于简化处理一组异步任务的执行和结果收集。它结合了Executor和BlockingQueue的功能&#xff0c;帮助管理任务的提交和完成。CompletionService的主要实现类是Exe…

前端必修技能:高手进阶核心知识分享 - CSS 阴影属性详解

CSS 涉及设计到阴影的相关内容包括三个方面&#xff1a;box-shadow属性&#xff08;盒子阴影&#xff09;、 text-shadow属性&#xff08;文本阴影&#xff09;、drop-shadow滤镜。 本篇文章旨在详细介绍和分析三种阴影的具体参数设置和典型用例。 box-shadow属性&#xff08;…

预防临床预测模型中可能的“算法歧视”

预防临床预测模型中可能的“算法歧视” 概要&#xff1a;如果训练数据中存在性别方面的不均衡&#xff0c;会让训练出的模型存在性别方面的“算法歧视”&#xff0c;进而导致某种性别下存在更多的误诊误治&#xff0c;最终造成医疗资源分配的不公平的伦理问题&#xff0c;导致模…

04.C1W3.Vector Space Models

往期文章请点这里 目录 Vector Space ModelsWord by Word and Word by DocWord by Document DesignWord by Document DesignVector Space Euclidean DistanceEuclidean distance for n-dimensional vectors Euclidean distance in PythonCosine Similarity: IntuitionCosine S…

STM32-SPI和W25Q64

本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. SPI&#xff08;串行外设接口&#xff09;通信1.1 SPI通信简介1.2 硬件电路1.3 移位示意图1.4 SPI时序基本单元1.5 SPI时序1.5.1 发送指令1.5.2 指定地址写1.5.3 指定地址读 2. W25Q642.1 W25Q64简介2.2 硬件电路2…

嵌入式C语言面试相关知识——内存管理(不定期更新)

嵌入式C语言面试相关知识——内存管理&#xff08;不定期更新&#xff09; 一、博客声明二、自问题目1、嵌入式系统的内存布局是怎么样的&#xff1f;2、动态内存分配在嵌入式系统中的使用有什么注意事项&#xff1f;3、什么是内存碎片&#xff0c;如何减少内存碎片&#xff1f…

win11自动删除文件的问题,安全中心提示

win11自动删除文件的问题&#xff0c;解决方法&#xff1a; 1.点击任务栏上的开始图标&#xff0c;在显示的应用中&#xff0c;点击打开设置。 或者点击电脑右下角的开始也可以 2.点击设置。也可以按Wini打开设置窗口。 3.左侧点击隐私和安全性&#xff0c;右侧点击Windows安全…

我国网络安全领域有哪些法律法规?主要内容是什么?

1. 背景介绍 网络信息安全方面的法规在全球范围内都有相应的立法&#xff0c;我们主要的立法有《网络安全法》、《密码法》、《数据安全法》以及《个人信息保护法》。当前也有一些相关的条例和管理办法&#xff0c;接下来就为大家一一介绍。 2. 法规介绍 在中国&#xff0c;…

多线程(进阶)

前言&#x1f440;~ 上一章我们介绍了线程池的一些基本概念&#xff0c;今天接着分享多线程的相关知识&#xff0c;这些属于是面试比较常见的&#xff0c;大部分都是文本内容 常见的锁策略 乐观锁 悲观锁 轻量锁 重量级锁 自旋锁 挂起等待锁 可重入锁和不可重入锁 互斥…

Leetcode 3207. Maximum Points After Enemy Battles

Leetcode 3207. Maximum Points After Enemy Battles 1. 解题思路2. 代码实现 题目链接&#xff1a;3207. Maximum Points After Enemy Battles 1. 解题思路 这一题的话其实关键在于说是想明白最优策略&#xff0c;事实上这道题的最优策略就是捡着最弱的enemy薅&#xff0c;…

接口测试分析、设计以及实现

接口相关理论 ui功能测试和接口测试哪个先执行&#xff1f;–为什么 结论&#xff1a;接口测试先执行 原因&#xff1a;ui功能测试需要等待前端页面开发完成、后台接口开发完后且前端与后端联调完成。ui功能测试与接口测试的区别&#xff1f; ui功能&#xff1a;功能调用&am…

学习笔记——交通安全分析14

目录 前言 当天学习笔记整理 5城市主干道交通安全分析 结束语 前言 #随着上一轮SPSS学习完成之后&#xff0c;本人又开始了新教材《交通安全分析》的学习 #整理过程不易&#xff0c;喜欢UP就点个免费的关注趴 #本期内容接上一期13笔记 当天学习笔记整理 5城市主干道交…