c++拷贝控制

文章目录

  • 拷贝构造函数的基本概念
    • 定义
    • 语法
    • 何时使用拷贝构造函数
    • 示例代码
    • 运行结果
    • 注意事项
  • 拷贝赋值运算符的基本概念
    • 定义
    • 语法
    • 何时使用拷贝赋值运算符
    • 示例代码
    • 运行结果
    • 注意事项
  • 析构函数的基本概念
    • 定义
    • 语法
    • 何时调用析构函数
    • 示例代码
    • 运行结果
    • 注意事项
  • 三/五法则
    • 三法则 (Rule of Three)
    • 五法则 (Rule of Five)
    • 示例:实现三/五法则
    • 注意事项
    • 使用场景
    • 示例代码
    • 注意事项
  • 阻止拷贝
    • 方法 1:删除拷贝构造函数和拷贝赋值运算符
    • 方法 2:继承自 `std::noncopyable`
    • 为什么要阻止拷贝
  • “行为像值”的类
    • 实现“行为像值”的类的关键点:
    • 示例代码
    • 注意事项
  • 定义行为像指针的类
    • 关键特征
    • 实现策略
    • 示例代码
    • 注意事项
  • 交换操作
    • 交换操作的基本概念
    • 实现交换操作
    • 完整的示例代码
    • 代码说明

拷贝构造函数的基本概念

定义

拷贝构造函数是一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。在 C++ 中,当对象以值传递的方式传入函数,或从函数返回时,或用一个对象初始化另一个对象时,拷贝构造函数会被调用。

语法

ClassName (const ClassName &old_obj);

何时使用拷贝构造函数

  1. 对象作为参数传递给函数(按值传递)
  2. 对象从函数返回(按值返回)
  3. 对象需要通过另一个对象进行初始化

示例代码

假设我们有一个简单的类 Point,它有一个拷贝构造函数。

#include <iostream>
using namespace std;class Point {private:int x, y;public:Point(int x1, int y1) { // 普通构造函数x = x1;y = y1;}// 拷贝构造函数Point(const Point &p2) {x = p2.x;y = p2.y;}int getX()            {  return x; }int getY()            {  return y; }
};int main() {Point p1(10, 15); // 普通构造函数被调用Point p2 = p1;    // 拷贝构造函数被调用cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();return 0;
}

运行结果

p1.x = 10, p1.y = 15
p2.x = 10, p2.y = 15

注意事项

  • 如果你没有为类定义拷贝构造函数,C++ 编译器会自动生成一个默认的拷贝构造函数。
  • 拷贝构造函数通常应该使用引用传递,以避免无限递归的拷贝操作。
  • 当类中包含指针成员时,可能需要深度拷贝。这需要自定义拷贝构造函数来确保每个成员正确地被复制。

拷贝赋值运算符在 C++ 中同样扮演着重要的角色,特别是在对象间赋值时。让我们详细探讨一下这个概念。

拷贝赋值运算符的基本概念

定义

拷贝赋值运算符用于将一个对象的值复制到另一个已经存在的对象中。每个类都有一个拷贝赋值运算符,可以是显式定义的,也可以是编译器自动生成的。

语法

对于类 ClassName,拷贝赋值运算符通常定义为:

ClassName& operator=(const ClassName& other);

何时使用拷贝赋值运算符

当使用赋值操作符(=)将一个对象的值赋给另一个已经存在的对象时,就会调用拷贝赋值运算符。例如:

ClassName obj1, obj2;
obj1 = obj2; // 这里调用了拷贝赋值运算符

示例代码

让我们以 Point 类为例,为其添加一个拷贝赋值运算符:

#include <iostream>
using namespace std;class Point {private:int x, y;public:Point(int x1, int y1) { // 构造函数x = x1;y = y1;}// 拷贝赋值运算符Point& operator=(const Point &p) {x = p.x;y = p.y;return *this;}int getX()            { return x; }int getY()            { return y; }
};int main() {Point p1(10, 15); // 构造函数被调用Point p2;         // 默认构造函数被调用p2 = p1;          // 拷贝赋值运算符被调用cout << "p2.x = " << p2.getX() << ", p2.y = " << p2.getY();return 0;
}

运行结果

p2.x = 10, p2.y = 15

注意事项

  • 拷贝赋值运算符应该检查自赋值的情况。
  • 与拷贝构造函数类似,当类中有指针成员时,需要考虑深拷贝。
  • 拷贝赋值运算符通常返回一个指向当前对象的引用,以允许链式赋值。

析构函数在 C++ 中是一个基本概念,用于管理对象销毁时的资源释放和清理工作。下面是关于析构函数的详细讲解。

析构函数的基本概念

定义

析构函数是一个特殊的成员函数,当对象生命周期结束时被自动调用。它的主要作用是释放对象占用的资源,例如释放分配给对象的内存、关闭文件等。

语法

对于类 ClassName,其析构函数的定义如下:

~ClassName();

它没有返回值,也不接受任何参数。

何时调用析构函数

析构函数会在以下情况被调用:

  1. 局部对象:当局部对象的作用域结束时(例如,函数执行完毕时)。
  2. 动态分配的对象:当使用 delete 操作符删除动态分配的对象时。
  3. 通过 delete[] 删除的对象数组:为数组中的每个对象调用。
  4. 程序结束:当程序结束时,为全局对象或静态对象调用。

示例代码

下面是一个简单的示例,演示如何定义和使用析构函数。

#include <iostream>
using namespace std;class Point {private:int x, y;public:Point(int x1, int y1) { // 构造函数x = x1;y = y1;cout << "构造函数被调用" << endl;}~Point() { // 析构函数cout << "析构函数被调用" << endl;}int getX()            { return x; }int getY()            { return y; }
};void createPoint() {Point p(10, 15); // 构造函数被调用cout << "Point created: " << p.getX() << ", " << p.getY() << endl;// 当createPoint函数结束时,p的析构函数被调用
}int main() {createPoint();return 0;
}

运行结果

构造函数被调用
Point created: 10, 15
析构函数被调用

注意事项

  • 析构函数不能被显式调用;它由 C++ 运行时自动调用。
  • 析构函数应该足够简单,避免在其中抛出异常。
  • 当类包含动态分配的资源时(如指针),通常需要在析构函数中释放这些资源,以防止内存泄露。

三/五法则(Rule of Three/Five)是 C++ 编程中的一个重要原则,它涉及类的拷贝控制成员:拷贝构造函数、拷贝赋值运算符和析构函数。这个法则帮助程序员处理资源管理,特别是在涉及动态内存分配时。

三/五法则

三法则 (Rule of Three)

如果你的类需要显式定义或删除以下任何一个成员,则它可能需要显式定义或删除所有三个:

  1. 拷贝构造函数
  2. 拷贝赋值运算符
  3. 析构函数

这是因为这三个函数通常涉及资源的分配和释放。例如,如果你的类动态分配内存,则需要确保在拷贝对象时正确地复制这些资源,并在对象销毁时释放资源。

五法则 (Rule of Five)

随着 C++11 的引入,新增了两个成员函数,扩展了三法则,成为五法则:

  1. 拷贝构造函数
  2. 拷贝赋值运算符
  3. 析构函数
  4. 移动构造函数
  5. 移动赋值运算符

这两个新成员函数用于支持移动语义,这在处理大型资源时是非常有用的,因为它允许资源的所有权从一个对象转移到另一个,而不是进行昂贵的拷贝。

示例:实现三/五法则

假设我们有一个类 ResourceHolder,它管理一个动态分配的数组。

#include <algorithm> // std::swap
#include <iostream>
using namespace std;class ResourceHolder {private:int* data;size_t size;public:// 构造函数ResourceHolder(size_t size): size(size), data(new int[size]) {}// 析构函数~ResourceHolder() { delete[] data; }// 拷贝构造函数ResourceHolder(const ResourceHolder& other): size(other.size), data(new int[other.size]) {std::copy(other.data, other.data + size, data);}// 拷贝赋值运算符ResourceHolder& operator=(ResourceHolder other) {swap(*this, other);return *this;}// 移动构造函数ResourceHolder(ResourceHolder&& other) noexcept : data(nullptr), size(0) {swap(*this, other);}// 移动赋值运算符ResourceHolder& operator=(ResourceHolder&& other) noexcept {if (this != &other) {delete[] data;data = nullptr;swap(*this, other);}return *this;}// 交换函数friend void swap(ResourceHolder& first, ResourceHolder& second) noexcept {using std::swap;swap(first.size, second.size);swap(first.data, second.data);}// 其他成员函数...
};int main() {ResourceHolder r1(10);ResourceHolder r2 = r1; // 拷贝构造函数ResourceHolder r3(15);r3 = std::move(r1); // 移动赋值运算符// ...
}

注意事项

  • 使用这些规则可以帮助避免资源泄漏、双重释放等问题。
  • 在实现移动构造函数和移动赋值运算符时,要注意处理自赋值情况,并确保符合异常安全的要求。

在 C++11 及更高版本中,=default 关键字的引入提供了一种简洁的方式来让编译器自动生成类的默认构造函数、析构函数、拷贝构造函数、拷贝赋值运算符、移动构造函数和移动赋值运算符。使用 =default 可以显式地告诉编译器我们希望使用它的默认实现,而不是完全禁用这些特殊的成员函数。

使用场景

  • 默认构造函数:当你希望类有一个默认构造函数,但不需要特别的实现时。
  • 拷贝构造函数和拷贝赋值运算符:当你希望类能被正常拷贝,且默认的逐成员拷贝行为是适当的。
  • 移动构造函数和移动赋值运算符:当你希望类支持移动语义,但不需要特别的移动逻辑。
  • 析构函数:当你希望类有一个默认的析构函数,通常在没有动态分配的资源需要清理时使用。

示例代码

下面的示例演示了如何使用 =default

#include <iostream>
#include <vector>
using namespace std;class MyClass {
public:MyClass() = default;                            // 默认构造函数~MyClass() = default;                           // 默认析构函数MyClass(const MyClass& other) = default;        // 默认拷贝构造函数MyClass(MyClass&& other) noexcept = default;    // 默认移动构造函数MyClass& operator=(const MyClass& other) = default; // 默认拷贝赋值运算符MyClass& operator=(MyClass&& other) noexcept = default; // 默认移动赋值运算符// 其他成员函数...
};int main() {MyClass obj1;               // 调用默认构造函数MyClass obj2 = obj1;        // 调用默认拷贝构造函数MyClass obj3 = std::move(obj1); // 调用默认移动构造函数// ...
}

注意事项

  • 使用 =default 时,编译器生成的成员函数是公共的、非虚的、非显式的,且具有相同的异常规范。
  • 如果类中有成员不可拷贝或不可移动,对应的拷贝或移动操作将被编译器删除。
  • 使用 =default 声明的函数可以在类定义中(此时为内联的)或类定义外声明。

在 C++ 中,阻止一个类被拷贝是一种常见的实践,尤其是对于那些管理独占资源的类。阻止拷贝可以确保对象的唯一性和资源管理的安全性。有两种主要方法来阻止类被拷贝:

阻止拷贝

方法 1:删除拷贝构造函数和拷贝赋值运算符

在 C++11 及以后的版本中,最简单的方式是使用 =delete 关键字明确地删除拷贝构造函数和拷贝赋值运算符。

class NonCopyable {
public:NonCopyable() = default; // 默认构造函数// 删除拷贝构造函数和拷贝赋值运算符NonCopyable(const NonCopyable&) = delete;NonCopyable& operator=(const NonCopyable&) = delete;// 允许移动构造函数和移动赋值运算符NonCopyable(NonCopyable&&) = default;NonCopyable& operator=(NonCopyable&&) = default;
};

方法 2:继承自 std::noncopyable

在 C++11 之前的版本中,或者在更喜欢这种方法的情况下,可以通过继承 boost::noncopyable 或自定义的非拷贝基类来实现。

#include <boost/noncopyable.hpp>class NonCopyable : private boost::noncopyable {// 类定义...
};

或者自定义一个非拷贝基类:

class NonCopyable {
protected:NonCopyable() = default;~NonCopyable() = default;NonCopyable(const NonCopyable&) = delete;NonCopyable& operator=(const NonCopyable&) = delete;
};class MyClass : private NonCopyable {// 类定义...
};

为什么要阻止拷贝

某些对象,比如文件句柄、数据库连接或者网络套接字,管理着不能简单复制的资源。在这些情况下,拷贝这样的对象可能会导致资源管理混乱(如多次释放同一资源),或者违反对象的唯一性约束。

通过阻止拷贝,你可以确保这类对象的实例保持唯一,并且避免了复制可能导致的问题。

在 C++ 中,创建一个“行为像值”的类意味着该类的实例在被拷贝时表现得就像基本数据类型(如 intdouble)那样。这通常涉及到实现深拷贝,确保每个对象都有自己的数据副本,从而使得对象之间相互独立。

“行为像值”的类

实现“行为像值”的类的关键点:

  1. 深拷贝:在拷贝构造函数和拷贝赋值运算符中实现深拷贝,以确保复制对象的数据而非仅复制指针或引用。
  2. 资源管理:确保管理动态分配的内存,防止内存泄漏。
  3. 拷贝赋值运算符:遵循赋值时的“拷贝并交换”惯用法,以确保异常安全和代码简洁。
  4. 析构函数:释放对象拥有的资源。

示例代码

假设我们有一个简单的 String 类,它包含一个动态分配的字符数组:

#include <cstring>
#include <algorithm>class String {
private:char* data;size_t length;void freeData() {delete[] data;}public:// 构造函数String(const char* str = "") : length(strlen(str)) {data = new char[length + 1];std::copy(str, str + length, data);data[length] = '\0';}// 拷贝构造函数String(const String& other) : length(other.length) {data = new char[length + 1];std::copy(other.data, other.data + length, data);data[length] = '\0';}// 拷贝赋值运算符String& operator=(String other) {swap(*this, other);return *this;}// 移动构造函数String(String&& other) noexcept : data(nullptr), length(0) {swap(*this, other);}// 析构函数~String() {freeData();}// 交换函数friend void swap(String& first, String& second) noexcept {using std::swap;swap(first.length, second.length);swap(first.data, second.data);}// 其他成员函数...
};

在这个例子中,String 类实现了深拷贝,确保每个 String 对象都独立拥有自己的字符数组。析构函数释放这些资源,而拷贝赋值运算符则使用“拷贝并交换”惯用法来确保异常安全。

注意事项

  • 确保深拷贝是必要的,并且能正确处理自赋值情况。
  • 考虑异常安全性,特别是在处理资源分配和释放时。
  • 为了提高效率,可以考虑实现移动构造函数和移动赋值运算符。

在 C++ 中,定义一个“行为像指针”的类通常意味着这个类的实例在被拷贝时表现得就像指针一样。这种行为通常涉及到共享数据,而不是像值类型那样进行深拷贝。这种模式经常通过实现引用计数或使用智能指针来实现。

定义行为像指针的类

关键特征

  1. 共享数据:实例之间共享相同的数据。
  2. 引用计数:通常通过引用计数来管理共享数据的生命周期。
  3. 自管理资源:类负责管理其指向的资源,如分配和释放内存。

实现策略

  1. 使用智能指针:如 std::shared_ptr,它自动处理引用计数和资源管理。
  2. 手动引用计数:在类内部手动实现引用计数机制。

示例代码

假设我们有一个简单的 SharedString 类,该类使用 std::shared_ptr 来共享字符串数据:

#include <iostream>
#include <string>
#include <memory>class SharedString {
private:std::shared_ptr<std::string> data;public:SharedString(const char* str = "") : data(std::make_shared<std::string>(str)) {}// 使用默认的拷贝构造函数、赋值运算符和析构函数SharedString(const SharedString&) = default;SharedString& operator=(const SharedString&) = default;~SharedString() = default;// 获取字符串const std::string& get() const {return *data;}// 其他成员函数...
};int main() {SharedString s1("Hello");SharedString s2 = s1; // s1 和 s2 共享相同的数据std::cout << "s1: " << s1.get() << ", s2: " << s2.get() << std::endl;return 0;
}

在这个例子中,SharedString 类的实例 s1s2 共享相同的字符串。通过使用 std::shared_ptr,当最后一个 SharedString 实例被销毁时,字符串数据会被自动释放。

注意事项

  • 当使用共享数据时,需注意线程安全和同步问题。
  • 确保所有的拷贝操作都正确地管理引用计数。
  • 考虑对象的所有权和生命周期,以防止悬挂指针或内存泄漏。

在 C++ 中,交换操作是一个重要的概念,尤其是在实现拷贝赋值运算符和移动构造函数时。正确的交换操作可以提高代码的效率和安全性。

交换操作

交换操作的基本概念

交换操作的目的是交换两个对象的状态。在许多情况下,这比通过传统的拷贝更高效,特别是对于大型对象或资源密集型对象。

实现交换操作

交换操作通常通过定义一个非成员函数 swap 来实现,该函数接受两个同类型对象的引用并交换它们的内部状态。

当然,我可以提供一个更完整的代码示例,展示如何在 C++ 类中实现交换操作。我们将创建一个简单的类 MyClass,该类将包含一些基本的成员变量和一个交换操作的实现。

完整的示例代码

#include <iostream>
#include <utility> // For std::swap (C++11 and later)class MyClass {
private:int* data;size_t size;public:// 构造函数MyClass(size_t size) : size(size), data(new int[size]) {for (size_t i = 0; i < size; ++i) {data[i] = i;  // 示例数据初始化}}// 拷贝构造函数MyClass(const MyClass& other) : size(other.size), data(new int[other.size]) {std::copy(other.data, other.data + size, data);}// 拷贝赋值运算符MyClass& operator=(MyClass other) {swap(*this, other);return *this;}// 移动构造函数MyClass(MyClass&& other) noexcept : MyClass() {swap(*this, other);}// 移动赋值运算符MyClass& operator=(MyClass&& other) noexcept {swap(*this, other);return *this;}// 析构函数~MyClass() {delete[] data;}// 交换成员函数friend void swap(MyClass& first, MyClass& second) noexcept {using std::swap;swap(first.size, second.size);swap(first.data, second.data);}// 用于演示的函数void print() const {for (size_t i = 0; i < size; ++i) {std::cout << data[i] << ' ';}std::cout << std::endl;}
};int main() {MyClass obj1(5);MyClass obj2(10);std::cout << "Original obj1: ";obj1.print();std::cout << "Original obj2: ";obj2.print();// 使用交换操作swap(obj1, obj2);std::cout << "Swapped obj1: ";obj1.print();std::cout << "Swapped obj2: ";obj2.print();return 0;
}
Original obj1: 0 1 2 3 4
Original obj2: 0 1 2 3 4 5 6 7 8 9
Swapped obj1: 0 1 2 3 4 5 6 7 8 9
Swapped obj2: 0 1 2 3 4

代码说明

  • 这个 MyClass 类包含一个动态分配的整型数组和一个表示数组大小的成员变量。

  • 实现了构造函数、拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符和析构函数。

  • 实现了一个 swap 函数,用于交换两个 MyClass 实例的内部状态。

  • main 函数中,创建了两个 MyClass 对象,并使用 swap 函数展示了交换操作的效果。

  • obj1 最初包含 5 个元素(从 0 到 4)。

  • obj2 最初包含 10 个元素(从 0 到 9)。

  • 执行交换后,obj1obj2 的内容互换。

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

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

相关文章

【SpringCloud】之配置中心(进阶使用)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringCloud开发之远程消费》。&#x1f3af;&a…

精彩推荐 |【Java技术专题】「重塑技术功底」攻破Java技术盲点之剖析动态代理的实现原理和开发指南(上)

攻破Java技术盲点之剖析动态代理的实现原理和开发指南 背景介绍静态代理和动态代理动态代理与静态代理的区别 进入正题重温&#xff1a;静态代理实现静态代理案例静态代理的弊端 重温&#xff1a;动态代理Java动态代理InvocationHandlerJava动态代理的实现下面看具体的代码实例…

Blazor项目如何调用js文件

以下是来自千问的回答并加以整理&#xff1a;&#xff08;说一句&#xff0c;文心3.5所给的回答不完善&#xff0c;根本运行不起来&#xff0c;4.0等有钱了试试&#xff09; 在Blazor项目中引用JavaScript文件&#xff08;.js&#xff09;以实现与JavaScript的互操作&#xff…

基于JAVA的服装店库存管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服装档案模块2.4 服装入库模块2.5 服装出库模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 服装档案表3.2.3 服装入库表3.2.4 服装出库表 四、系统展示五、核心代码5.…

Java 11中的新字符串APIs详解

第1章 引言 大家好&#xff0c;我是小黑&#xff0c;咱们都知道&#xff0c;Java作为一种广泛使用的编程语言&#xff0c;每一次更新都会带来不少新鲜事物。而Java 11&#xff0c;作为长期支持&#xff08;LTS&#xff09;版本之一&#xff0c;更是引起了广大开发者的关注。好…

并发(8)

目录 46.Thread.sleep(),Object.wait(),Condition.await(),LockSupport.part()的区别&#xff1f; 47.如果在wait&#xff08;&#xff09;之前执行了notify&#xff08;&#xff09;会怎样&#xff1f; 48.如果在park()之前执行了unpark()会怎样&#xff1f; 49.什么是AQS&…

九州金榜|孩子厌学,作为父母有想做自己的原因吗?

孩子不会天生就厌学&#xff0c;如果孩子天生厌学&#xff0c;那么孩子就不可能学会说话&#xff0c;走路&#xff0c;日常生活&#xff0c;更不可能去上学&#xff0c;孩子厌学因素非常多&#xff0c;而作为父母&#xff0c;你有没有想过是你的原因造成的呢&#xff1f;九州金…

编程语言的未来,通用代码生成器和超级语言

编程语言的未来&#xff0c;通用代码生成器和超级语言 我们生活在一个编程语言种类繁多&#xff0c;百花齐放的年代。形形色色的编程语言覆盖了软件开发的方方面面。如果说这些变成语言有什么共性的话&#xff0c;大家都知道&#xff0c;大多数编程语言是高级语言。 何为高级…

K8S---通过curl访问api

1、列出所有的接口 curl --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/default-admin.pem --key /opt/kubernetes/ssl/default-admin-key.pem https://10.240.184.74:6443/ 2、列出核心api的版本 curl --cacert /opt/kubernetes/ssl/ca.pem -…

手敲MyBatis(十四章)-解析含标签的动态SQL语句

1.前言 这一章主要的就是要解析动态标签里的Sql语句&#xff0c;然后进行条件语句的拼接&#xff0c;动态标签实现了trim和if标签&#xff0c;所以Sql节点就要加上TrimSqlNode和ifSqlNode&#xff0c;我们最终要获取Sql源&#xff0c;动态Sql语句需要一些处理&#xff0c;所以…

SpringCloud系列篇:核心组件之声明式HTTP客户端组件【远程消费】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于SpringCloud的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一. 远程消费组件是什么 二. 远程消…

竞赛练一练 第22期:NOC大赛每日一练,python题目刷题第7天,包含答案解析

题目来自:NOC大赛创客智慧编程赛项Python 复赛模拟题(一) 第一题: 编写一个彩票游戏:随机生成一个不重复的五位数作为彩票号,游戏提示用户输入一个五位整数,然后根据下面的规则判断用户是否能赢得奖金,最后要求输出彩票号和奖金。 (1)若用户输入的数字和彩票的数字完…

c语言考试代码

文章目录 文件操作题 “%”&#xff1a;格式化字符串的起始标志。 “0”&#xff1a;表示使用零来填充输出字段的宽度。 “4”&#xff1a;表示输出字段的宽度为4个字符&#xff0c;如果输出的十六进制数不足4位&#xff0c;则在左边用零进行填充。 “x”&#xff1a;表示以十六…

MySql海量数据存储与优化

一、Mysql架构原理和存储机制 1.体系结构 2.查询缓存 3.存储引擎 存储引擎的分类 innodb&#xff1a;支持事务&#xff0c;具有支持回滚&#xff0c;提交&#xff0c;崩溃恢复等功能&#xff0c;事务安全myisam:不支持事务和外键&#xff0c;查询速度高Memory&#xff1a;利…

Spring Boot实现数据加密脱敏:注解 + 反射 + AOP

文章目录 1. 引言2. 数据加密和脱敏的需求3. Spring Boot项目初始化4. 敏感数据加密注解设计5. 实现加密和脱敏的工具类6. 实体类和加密脱敏注解的使用7. 利用AOP实现加密和脱敏8. 完善AOP切面9. 测试10. 拓展功能与未来展望10.1 加密算法的选择10.2 动态注解配置 11. 总结 &am…

CHS_02.1.1.2+操作系统的特征

CHS_02.1.1.2操作系统的特征 操作系统的四个特征并发这个特征为什么并发性对于操作系统来说是一个很重要的基本特性资源共享虚拟异步性 各位同学 大家好 在这个小节当中 我们会学习 操作系统的四个特征 操作系统有并发 共享 虚拟和异部这四个基本的特征 其中 并发和共享是两个…

机器人的末端执行器由什么零件组成,有什么作用。

问题描述&#xff1a;机器人的末端执行器由什么零件组成&#xff0c;有什么作用。 问题解答&#xff1a; 机器人的末端执行器是机器人机械结构的最末端部分&#xff0c;用于执行具体的任务和与环境进行交互。末端执行器通常由多个零部件组成&#xff0c;其主要作用是完成机器…

log4j RCE漏洞原理分析及检测

实现原理 log4j支持使用表达式的形式打印日志&#xff0c;比如 logger.info("system propety: ${sys:user.dir}");问题就在与表达式支持非常多样&#xff0c;其中有一个jndi就是今天的主题 logger.info("system propety: ${jndi:schema://url}");jdk将从…

学习笔记——C++ do while语句

作用&#xff1a;满足循环条件&#xff0c;执行循环语句 语法&#xff1a;do{循环语句}while{循环条件}&#xff1b; 注意&#xff1a;与while的区别在于do while 会先执行一次循环语句&#xff0c;再判断循环条件。 示例&#xff1a;打印0-9的数字 #include<bits/stdc.h…

图神经网络|9.3 邻接矩阵的变换

由于邻接矩阵中一般不会&#xff08;i,i&#xff09;等于1&#xff0c;除非第i个点上有自环。 而如果用邻接矩阵去乘上特征矩阵&#xff0c;那么将丢失自身向自身的贡献。 此时可以再邻接矩阵的基础上&#xff0c;再加上一个单位阵&#xff0c;从而使得最终的结果包含自身对整体…