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

【C++篇】string类的终章:深浅拷贝 + 模拟实现string类的深度解析(附源码)

💬 欢迎讨论:在阅读过程中有任何疑问,欢迎在评论区留言,我们一起交流学习!
👍 点赞、收藏与分享:如果你觉得这篇文章对你有帮助,记得点赞、收藏,并分享给更多对C++感兴趣的朋友


文章目录

  • 前言
  • 深浅拷贝
    • 一、浅拷贝
      • 浅拷贝的问题
    • 二、深拷贝
      • 深拷贝的实现
        • **拷贝构造函数**
        • **赋值运算符**
    • 三、 总结
  • 模拟实现string类
    • 一、核心结构设计
      • 1. 成员变量
    • 二、关键功能实现
      • 1. 构造函数与拷贝控制
        • 1.1构造函数
        • 1.2拷贝构造函数与赋值运算符
        • 1.3析构函数
      • 2. 迭代器
      • 3. capacity相关接口
        • 3.1`reserve`扩容
        • 3.2`resize`调整大小
        • 3.3`size`获取大小
        • 3.4`capacity`获取容量
        • 3.5`empty`判空
      • 4. modify相关接口
        • 4.1`push_back`尾插字符
        • 4.2`append`在字符串结尾拼接字符串
        • 4.3`operator+=`
        • 4.4`clear` 清空有效字符
        • 4.5`c_str`获取C属性的字符串(字符串指针)
      • 5. assess接口
      • 6. 字符串操作
        • 6.1`find`查找字符或子串
        • 6.2`insert`插入
          • 6.2.1 插入字符
          • 6.2.2 插入字符串
        • 6.3`erase`删除
        • 6.4`substr`截取字符串
      • 7. 关系运算符重载
      • 8. 流插入与流提取运算符重载


前言

本文将通过一个自定义的字符串类实现(zhh::string zhh是一个我自定义的作用域),深入探讨string类的核心设计思路与实现细节,以及为什么在拷贝构造和赋值运算符重载的实现需要用深拷贝。该代码模拟了标准库std::string的一些核心功能,包括动态内存管理、迭代器、常用操作符重载等。同时也借此对深浅拷贝进行实际上的应用。

源码在文章末尾


深浅拷贝

一、浅拷贝

浅拷贝:也称位拷贝,仅仅这是将值拷贝过来。

我们平时C语言中使用的赋值,以及函数传值,都是浅拷贝。

如果在对象中使用,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问错误。

浅拷贝的问题

浅拷贝 仅复制指针的值(地址),导致多个对象共享同一块内存:

class String {
public:char* data;// 假设有浅拷贝构造函数String(const String& other) : data(other.data) {}
};String s1("Hello");
String s2 = s1; // 浅拷贝:s2.data 和 s1.data 指向同一内存

问题

  • 修改冲突:修改 s1 会影响 s2
  • 重复释放:析构时 s1s2 会尝试释放同一块内存,导致崩溃。
  • 悬空指针:一个对象被析构后,另一个对象的指针失效。

那么,如何使得每个对象都有一份独立的资源,不要和其他对象共享


二、深拷贝

深拷贝 是一种内存管理技术,其核心是完整复制对象及其动态分配的资源,生成一个与原对象完全独立的新对象。在C++中,当类包含指针成员并指向堆内存时,必须通过深拷贝来避免以下问题:

深拷贝的实现

深拷贝通过分配新内存并复制内容,确保对象间的独立性。

在实现string类中的拷贝构造函数以及赋值运算符重载都需要用到深拷贝:

拷贝构造函数
string(const string& s) {_str = new char[s._capacity + 1]; // 分配新内存memcpy(_str, s._str, s._size + 1); // 复制内容(包含'\0')_size = s._size;_capacity = s._capacity;
}
  • 关键点:为新对象分配独立的内存,并复制原对象的所有数据。
赋值运算符

传统写法

string& operator=(const string& s)
{if (this != &s){char* tmp = new char[s._capacity + 1];memcpy(tmp, s._str, s._size+1);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;
}
  • 深拷贝的经典写法

现代写法

//string类的swap接口:将每个成员变量交换即可
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}string& operator=(const string& s)
{if (this != &s){string tmp(s);swap(tmp);}return *this;
}
  • 利用拷贝构造,用s构造tmp——代替new开辟空间与memcpy拷贝步骤
  • 再将*thistmp交换数据,利用局部对象tmp出函数作用域调用析构释放原*this的数据——代替delete释放原空间数据步骤

省略了大量代码,妙不可言

当今写法

string& operator=(string tmp) {swap(tmp); // 交换资源,tmp 析构时释放原内存return *this;
}

点睛之笔利用函数传值自动调用拷贝构造的原理——替代了手动调用拷贝构造

真是进了米奇妙妙屋了,妙的不能再妙了😏

  • 优势:利用临时对象 tmp 的深拷贝,通过 swap 安全交换资源,天然避免自赋值问题。

三、 总结

  • 深拷贝是管理动态资源的类的必备实现,确保对象间的独立性。
  • 浅拷贝仅适用于不涉及资源所有权的简单数据类型(如 int, double)。
  • 在C++中,默认拷贝构造函数和赋值运算符是浅拷贝。若类需要深拷贝,必须手动实现拷贝控制函数(拷贝构造、赋值运算符、析构函数)。

通过正确实现深拷贝,可以避免内存泄漏、悬空指针和不可预测的行为,从而编写出健壮的C++程序。


模拟实现string类

一、核心结构设计

1. 成员变量

class string {
private:char* _str;       // 字符串内容size_t _capacity; // 总容量(不包含'\0')size_t _size;     // 当前长度const static size_t npos; // 特殊值,表示无效位置
};
  • _str:动态分配的字符数组,存储字符串内容(以'\0'结尾)。
  • _capacity:当前分配的内存容量(不包含'\0'的额外空间)。
  • _size:字符串实际长度(包含'\0'的额外空间)。
  • npos:静态常量,表示无效位置(定义为-1的无符号最大值)。

二、关键功能实现

1. 构造函数与拷贝控制

1.1构造函数
string(const char* str = "") {_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];memcpy(_str, str, _size);_str[_size] = '\0';
}
  • 支持从C风格字符串构造,默认构造空字符串。
  • 使用memcpy高效拷贝数据,但需确保输入字符串以'\0'结尾。
1.2拷贝构造函数与赋值运算符

前文在讲解深拷贝时已详细介绍了,这里不再赘述了。

string(const string& s) {_str = new char[s._capacity + 1]; // 需分配足够空间(原实现有误)memcpy(_str, s._str, s._size + 1); // 包含'\0'_size = s._size;_capacity = s._capacity;
}string& operator=(string tmp) {swap(tmp); // 通过交换资源实现赋值return *this;
}
1.3析构函数
~string() {delete[] _str;_str = nullptr;_size = _capacity = 0;
}
  • 释放动态内存,重置成员变量。

2. 迭代器

typedef char* iterator;
typedef const char* const_iterator;iterator begin() 
{ return _str; 
}iterator end() 
{ return _str + _size;
}const_iterator begin()const
{return _str;
}const_iterator end()const
{return _str + _size;
}
  • 提供类似标准库的迭代器接口,支持范围for循环。

测试代码:

void TestString1()
{zhh::string s1("hello world");zhh::string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";it++;}cout << endl;{for (auto ch : s1)cout << ch << "6";}cout << endl;
}

测试结果:
在这里插入图片描述


3. capacity相关接口

3.1reserve扩容
void reserve(size_t n) {if (n > _capacity) {char* tmp = new char[n + 1];memcpy(tmp, _str, _size + 1); // 包含'\0'delete[] _str;_str = tmp;_capacity = n;}
}
  • 当给定的目标扩容值小于已有容量,容量保持不变。
  • 使用memcpy提升性能,但需确保拷贝长度包含'\0'
3.2resize调整大小
void resize(size_t n, char c = '\0') {if (n > _size) {reserve(n);for (size_t i = _size; i < n; ++i)_str[i] = c;}_str[n] = '\0'; // 确保终止符_size = n;
}
  • n > _size,填充字符c;否则截断字符串。
3.3size获取大小
size_t size()const
{return _size;
}
3.4capacity获取容量
 size_t capacity()const{return _capacity;}
3.5empty判空
bool empty()const
{return _size == 0 ? true : false;
}

4. modify相关接口

4.1push_back尾插字符

尾插字符很简单,在字符串尾部赋值即可,需要考虑的问题有:

  • 容量是否足够?是否需要扩容?
  • 如何扩?扩多大?
  • 字符串结尾有没有附上\0结束标志?

解决问题:

  • 检查容量方法:判断_capacity是否大于_size +1
  • 扩容方式:调用reserve扩容。扩多大取决于自己,我这里是扩两倍
  • 记得改变_size的值,最后在_size的位置赋上\0

代码:

void push_back(char c)
{//检查容量,不足就扩if (_capacity < _size + 1){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = c;++_size;_str[_size] = '\0';
}
4.2append在字符串结尾拼接字符串

思路与尾插字符差不多:检查容量->不足就扩->拼接->改变size
需要注意的是:

  • 函数参数是指针,assert断言防止空指针传入
  • 拼接方法:使用memcpy从字符串尾部开始拷贝
  • size加上传入字符串长度即可
  • size位置无需赋值\0,因为memcpy时可以顺带完成

代码:

 void append(const char* str){assert(str);size_t len = strlen(str);reserve(_size + len);memcpy(_str + _size, str, len);_size += len;}
4.3operator+=

这里复用push_back和append即可:

string& operator+=(char c)
{push_back(c);return *this;
}string& operator+=(const char* str)
{append(str);return *this;
}

💡:我们平时需要尾插字符或字符串时一般都用这个接口,因为他完全可以代替push_back和append。但我们需要知道他的底层其实是由push_back和append实现的。

4.4clear 清空有效字符

_str[0]的位置赋值\0即可

 void clear(){_str[0] = '\0';_size = 0;}
4.5c_str获取C属性的字符串(字符串指针)

返回_str成员变量即可

const char* c_str()const
{return _str;
}

5. assess接口

这个其实我们前面已经用过了,是不是有种自来熟的感觉😂
返回字符串对应位置的字符即可

char& operator[](size_t index)
{assert(index < _size && index >= 0);return _str[index];
}const char& operator[](size_t index)const
{assert(index < _size && index >= 0);return _str[index];
}

6. 字符串操作

6.1find查找字符或子串
  • 查找字符:遍历查询即可
  • 查找子串:使用strstr库函数实现高效查找。

代码:

// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{assert(pos >= 0);for (size_t i = pos; i < _size; ++i){if (_str[i] == c){return i;}}return npos;
}// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{char* ptr = strstr(_str + pos, s);if (ptr){return ptr - _str;}else{return npos;}}
6.2insert插入
6.2.1 插入字符

大体与push_back类似,但多了一个步骤——挪动数据
从尾部开始,将字符赋值到后面的一个位置,到pos位置结束(pos位置也要挪动)。

代码(有bug):

 // 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c){assert(pos < _size && pos >= 0);//检查容量,不足就扩if (_capacity < _size + 1){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;while (pos <= end){_str[end + 1] = _str[end];--end;}_str[pos] = c;++_size;return *this;}

聪明的你可以看出问题出在哪吗?
可以试试用pos = 0运行一下代码,会发现超时了,那应该就是循环的问题,我们锁定while的位置
走读代码,当end = 0,进入循环,–end得end = -1,end的类型是size_t,而-1是最大的无符号整数,所以我们循环进入了死循环。

处理方法:添加循环条件end != npos

正确代码:

 // 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c){assert(pos < _size && pos >= 0);//检查容量,不足就扩if (_capacity < _size + 1){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;//当pos = 0 ,end会变为-1,成为最大无符号整数进入死循环//因此这里需补充条件end != nposwhile (pos <= end && end != npos){_str[end + 1] = _str[end];--end;}_str[pos] = c;++_size;return *this;}
6.2.2 插入字符串

插入字符串的挪动方式差不多:
方式是相同的,但挪动距离为len(插入的字符串长度)

代码:

string& insert(size_t pos, const char* str){assert(pos < _size && pos >= 0);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}//挪动数据size_t end = _size;//当pos = 0 ,end会变为-1,成为最大无符号整数进入死循环//因此这里需补充条件end != nposwhile (end >= pos && end != npos){_str[end + len] = _str[end];--end;}for (size_t i = pos; i < pos + len; ++i){_str[i] = *str;++str;}_size += len;return *this;}
6.3erase删除
  1. 如果需要删除的长度大于_size - pos,直接在pos位置赋值\0即可
  2. 否则,将需要删除的部分挪到\0后面即可
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len)
{if (len >= _size - pos){_str[pos] = '\0';}else{size_t end = pos + len;for (size_t i = end; i <= _size; ++i){_str[end - len] = _str[end];end++;}}return *this;
}
6.4substr截取字符串

核心思想:创建一个临时对象,将需的字符依次尾插到这个对象,最后返回这个对象的值即可
注意:这里只能传值返回

string substr(size_t pos = 0, size_t len = npos)
{string tmp;size_t n = len;if (n > _size - pos || n == npos){n = _size - pos;}tmp.reserve(n);for (size_t i = pos; i < pos + n; ++i){tmp += _str[i];}//返回值传值,调用拷贝构造return tmp;//返回值不可用引用,否则出作用域调用析构,非法访问
}

7. 关系运算符重载

一共有6个关系运算符:< > == <= >= !=
其实我们只需要实现两个即可(<>==

核心问题:如何比较两个字符串大小?

在编程中,两个字符串的大小比较通常是基于字典序进行的,类似于字典中单词的排列顺序。具体规则如下:

比较规则

  1. 逐字符比较

    • 从左到右依次比较两个字符串的每个字符的ASCII码值
    • 如果某位置的字符不同,直接根据这两个字符的ASCII码值大小决定字符串大小。
      示例
      "apple""apricot"
      第3个字符 'p'(ASCII 112) < 'r'(ASCII 114),所以 "apple" < "apricot"
  2. 长度比较

    • 如果所有对应字符均相同,则较长的字符串更大。
      示例
      "hello""hell":前4字符相同,但前者更长,所以 "hello" > "hell"
  3. 完全相等

    • 如果所有字符相同且长度相同,则两字符串相等。

实现思路:

  • 先比较短字符串长度较短的长度,如果谁大,那么谁就大;如果相等,那么字符串长度较长的字符串更大,如果字符串长度也相等,那么相等。
 bool operator<(const string& s){int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);return ret == 0 ? _size < s._size : ret < 0;}bool operator==(const string& s){return _size == s._size && memcmp(_str, s._str, _size) == 0;}bool operator<=(const string& s){return *this < s || *this == s;}bool operator>(const string& s){return !(*this <= s);}bool operator>=(const string& s){return !(*this < s);}bool operator!=(const string& s){return !(*this == s);}

8. 流插入与流提取运算符重载

我们上一期知道:这两个运算符重载不能作为成员函数(为了使得对象参数作为第二个参数),需要在类外实现,必要的话可以将它们声明为类的友元。

流插入比较简单,遍历流插入字符即可:

ostream& operator<<(ostream& out, const zhh::string& s)
{for (auto ch : s){out << ch;}return out;
}

而流提取就比较麻烦了。

  1. 每次输入都需要清空字符串(使用clear)。
  2. 清理缓存区的空格与换行(清除输入的字符串头部的空格与换行)。
  3. 实现时不能用流提取,否则会造成流堵塞(输入空格与换行无法结束输入),使用get()可以解决这个问题
  4. 如果我们一次输入较多数据会造成频繁扩容,效率低下
    我们可以用数组来解决这个问题:
    定义一个buff[128],我们将输入的数据存入数组,
    情况1:输入结束且数组未满,直接将数组中的字符尾插到目标字符串。
    情况2:输入未结束且数组已满,将数组中的字符尾插到目标字符串,继续从数组的起始位置重新存输入的数据,循环往复。
    注意:每次要尾插数据前,要在数组的有效数据后赋值\0,否则会造成数据错乱。
 istream& operator>>(istream& in, zhh::string& s){//清理原数据s.clear();char ch = '\0';//清理缓存区的空格与换行while (ch == ' ' || ch == '\n'){ch = in.get();}char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){ch = in.get();buff[i++] = ch;if (i == 127)//留一个位置放\0{buff[i] = '\0';s += buff;i = 0;}}if (i != 0){buff[i] = '\0';s += buff;}return in;}
}

源码自取:模拟实现string类

万字文章,制作不易,留给赞再走吧~
在这里插入图片描述

相关文章:

【C++篇】string类的终章:深浅拷贝 + 模拟实现string类的深度解析(附源码)

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对C感兴趣的…...

线性DP:最短编辑距离

Dp 状态表示 f&#xff08;i&#xff0c;j&#xff09; 集合所有将A[1~i]变成B[1~j]的操作方式属性min 状态计算 &#xff08;划分&#xff09; 增f(i,j)f(i,j-1)1//A[i]元素要增加&#xff0c;说明A前i位置与B前j-1相同删f(i,j)f(i-1,j)1//A[i]元素要删除&#xff0c;说明A前i…...

【全网最全】23种设计模式思维导图详解 | 含React/Vue/Spring实战案例

【全网最全】23种设计模式思维导图详解 | 含React/Vue/Spring实战案例 导图概述 本文通过高清思维导图系统梳理了23种设计模式&#xff0c;分为创建型、结构型、行为型三大类&#xff0c;并标注了各模式在主流框架&#xff08;如React、Vue、Spring&#xff09;中的典型应用场…...

8086微机原理与接口技术复习(1)存储器(2)接口

8086微机原理与接口技术复习&#xff08;1&#xff09;存储器&#xff08;2&#xff09;接口 存储器8086的存储空间存储器的拓展存储器的分类 接口8086I/O82558253串行通信 存储器 我们上的是嵌入式与接口技术这门课&#xff0c;存储器章节重点在于理解8086CPU的存储体结构&am…...

第 6 篇:衡量预测好坏 - 评估指标

第 6 篇&#xff1a;衡量预测好坏 - 评估指标 上一篇&#xff0c;我们小试牛刀&#xff0c;用朴素预测、平均法、移动平均法和季节性朴素预测这几种简单方法对未来进行了预测。我们还通过可视化将预测结果与真实值进行了对比。 但光靠眼睛看图来判断“哪个预测更好”往往是不…...

极刻AI搜v1.0 问一次问题 AI工具一起答

软件名&#xff1a;极刻AI搜 版本&#xff1a;v1.0 功能&#xff1a;囊括了互联网上比较好用的一些支持”搜索“的网站或者工具 开发平台&#xff1a;nodepythonweb 分类有&#xff1a; AI搜索&#xff08;支持智能问答的AI搜索引擎&#xff09; 常规搜索&#xff1a;&#xff…...

单片机 + 图像处理芯片 + TFT彩屏 进度条控件

进度条控件使用说明 概述 本进度条控件基于单片机 RA8889/RA6809 TFT开发&#xff0c;提供了简单易用的进度显示功能。控件支持多个进度条同时显示、自定义颜色、边框和标签等特性&#xff0c;适用于需要直观显示进度信息的各类应用场景。 特性 支持多个进度条同时显示可…...

RHCSA Linux系统 用户和组的管理

用户管理&#xff1a;增useradd 删userdel 改usermod 查id 组的管理&#xff1a;增groupadd 删groupdel 改groupmod 查groups /etc/default/useradd 即定义useradd默认参数&#xff0c;也定义了/etc/passwd &#xff0c;/etc/shadow&#xff0c;/etc/group&#…...

2025年pta团队设计天梯赛题解

题解不全&#xff0c;望见谅 L1-1 珍惜生命 题目 前辈工程师 Martin Golding 教育我们说&#xff1a;“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”&#xff08;写代码的时候&#xff0c;总是要…...

Vue---vue2和vue3的生命周期

核心生命周期对比 生命周期阶段Vue 2 钩子Vue 3 Composition API​​初始化​​beforeCreate无&#xff08;使用 setup() 替代&#xff09;​​初始化完成​​created无&#xff08;使用 setup() 替代&#xff09;​​挂载前​​beforeMountonBeforeMount​​挂载完成​​moun…...

C#常用LINQ

在开发时发现别人的代码使用到了LINQ十分便捷且清晰&#xff0c;这里记录一下常用LINQ和对应的使用。参考链接&#xff1a;LINQ 菜鸟教程 使用的学生类和字符串用于测试 public class Student {public int StudentID;public string StudentName;public int Age; }Student[] st…...

Java--数组的应用

一、数组的地址值 数组的地址值表示数组在内存中的位置。 [I1eb44e46 [ &#xff1a;表示当前是一个数组I&#xff1a;表示当前数组是int类型&#xff1a;表示一个间隔符号&#xff08;固定格式&#xff09;1eb44e46&#xff1a;数组真正的地址值&#xff08;十六进制&#…...

PostgreSQL基础

一、PostgreSQL介绍 PostgreSQL是一个功能强大的 开源 的关系型数据库。底层基于C实现。 PostgreSQL的开源协议和Linux内核版本的开源协议是一样的。。BDS协议&#xff0c;这个协议基本和MIT开源协议一样&#xff0c;说人话&#xff0c;就是你可以对PostgreSQL进行一些封装&a…...

Linux系统管理与编程13:基于CentOS7.x的LAMP环境部署

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 一、实验目标 1.理解Apache服务器原理 2.掌握Apache服务器的配置文件 3.具备安装Mysql数据库能力 4.具备安装Apache服务器能力 5.具备PHP与数据库连接能力 6.具备Apache、Mysql、…...

浅谈AI致幻

文章目录 当前形势下存在的AI幻觉&#xff08;AI致幻&#xff09;什么是AI幻觉AI幻觉的类型为什么AI会产生幻觉AI幻觉的危害与影响当前应对AI幻觉的技术与方法行业与学术界的最新进展未来挑战与展望结论 当前形势下存在的AI幻觉&#xff08;AI致幻&#xff09; 什么是AI幻觉 …...

【架构】-- StarRocks 和 Doris 介绍与选型建议

StarRocks 和 Doris 的介绍 随着大数据分析需求的不断增长,企业对高性能、低延迟的分析型数据库提出了更高的要求。StarRocks 和 Apache Doris 是当前主流的开源 MPP(Massively Parallel Processing)数据库系统,广泛应用于实时分析、报表生成和数据仓库等场景。本文将从架…...

【SF顺丰】顺丰开放平台API对接(注册、API测试篇)

1.注册开发者账号 注册地址&#xff1a;顺丰企业账户中心 2.登录开发平台 登录地址&#xff1a;顺丰开放平台 3.开发者对接 点击开发者对接 4.创建开发对接应用 开发者应用中“新建应用”创建应用&#xff0c;最多创建应用限制数量5个 注意&#xff1a;需要先复制保存生产校验…...

C语言高频面试题——常量指针与指针常量区别

1. 常量指针&#xff08;Pointer to Constant&#xff09; 定义&#xff1a; 常量指针是指向一个常量数据的指针&#xff0c;即指针指向的内容不能通过该指针被修改。 语法&#xff1a; const int* ptr;或者&#xff1a; int const* ptr;解释&#xff1a; const修饰的是指…...

Novartis诺华制药社招入职综合能力测评真题SHL题库考什么?

一、综合能力测试 诺华制药的入职测评中&#xff0c;综合能力测试是重要的一部分&#xff0c;主要考察应聘者的问题解决能力、数值计算能力和逻辑推理能力。测试总时长为46分钟&#xff0c;实际作答时间为36分钟&#xff0c;共24题。题型丰富多样&#xff0c;包括图形变换题、分…...

网页下载的m3u8格式文件使用FFmpeg转为MP4

FFmpeg 是一个强大的开源音视频处理工具&#xff0c;可以直接将 M3U8 合并并转换为 MP4。 1.步骤&#xff1a; 下载 FFmpeg 官网&#xff1a;https://ffmpeg.org/ Windows 用户可以直接下载 静态构建版本&#xff08;Static Build&#xff09;&#xff0c;解压后即可使用。 2…...

Java 并发包核心机制深度解析:锁的公平性、异步调度、AQS 原理全解

&#x1f9e0; Java 并发包核心机制深度解析&#xff1a;锁的公平性、异步调度、AQS 原理全解 Java 并发编程的地基是 java.util.concurrent&#xff0c;但真正驱动这个系统的&#xff0c;是它背后隐藏的三根支柱&#xff1a; ReentrantLock 的公平/非公平调度策略Completabl…...

μC/OS 版本演进过程 | uC/OS-II 和 uC/OS-III 有什么区别?

uC/OS 系列是由 Jean J. Labrosse 开发的一套嵌入式实时操作系统&#xff08;RTOS&#xff09;&#xff0c;以其高质量源码和清晰的结构&#xff0c;在嵌入式教学和某些工业项目中有着广泛影响。该系统主要包含两个版本&#xff1a;uC/OS-II 和 uC/OS-III。 本文将带你了解这两…...

永磁同步电机参数辨识算法--递推最小二乘法辨识

一、原理介绍 最小二乘法大约是1795年高斯在其著名的星体运动轨道预报研究工作中提出的。后来&#xff0c;最小二乘法成为了估计理论的基石。最小二乘法由于原理简明、收敛较快、易于编程实现等特点&#xff0c;在系统参数估计中应用相当广泛。 其基本原理为&#xff1a; 改写…...

树莓派5+L298N控制电机

准备工作: 树莓派5开发板L298N 控制板电机1个12v的电池1个杜邦线若干L298N 引脚介绍 (1)图中标注的1和2都是都在输出引脚,可以各接入一个电机,电机不分正负极,随便接 (2)图中3这里是控制板的电源输入正负极,可以输入5v和12v,我这里输入的是12v电源,使用的时候应该把…...

UofTCTF-2025-web-复现

感兴趣朋友可以去我博客里看&#xff0c;画风更好看 UofTCTF-2025-web-复现 文章目录 scavenger-huntprismatic-blogscode-dbprepared-1prepared-2timeless scavenger-hunt 国外的一些ctf简单题就喜欢把flag藏在注释里&#xff0c;开源代码找到第一部分的flag 抓个包返回数据…...

记录seatunnel排查重复数据的案例分析

文章目录 背景分析检查现象检查B集群是否有异常&#xff0c;导致重复消费的分析同步任务 修复问题发现flink job 一直报异常修复问题 背景 使用seatunnel 同步数据从A 集群kafka 同步到B集群kafka,现象是发现两边数据不一致&#xff0c;每天10w级别会多几十条数据 分析 检查…...

技术速递|Agent 模式:对所有用户开放,并支持 MCP

作者&#xff1a;Isidor Nikolic 翻译/排版&#xff1a;Alan Wang Agent 模式正在向所有 VS Code 用户推广&#xff01;它充当一个自主的配对编程助手&#xff0c;能够根据你的指令执行多步编码任务&#xff0c;例如分析代码库、提出文件修改建议以及运行终端命令。它能够响应编…...

实验四 Java图形界面与事件处理

实验四 Java图形界面与事件处理 ###实验目的 掌握Java语言中AWT和Swing组件的基本用法掌握Java语言中的事件处理方法掌握Java语言中事件源、监视器和处理事件的接口的概念 ###实验内容 图形用户界面设计程序(ArtFont.java)&#xff08;90分&#xff09; 要求&#xff1a;设…...

day2 python训练营

浙大疏锦行 python训练营介绍...

Linux下 REEF3D及DIVEMesh 源码编译安装及使用

目录 软件介绍 基本依赖 一、源码下载 1、REEF3D 2、DIVEMesh 二、解压缩 三、编译安装 1、REEF3D 2、DIVEMesh 四、算例测试 软件介绍 REEF3D是一款开源流体动力学框架&#xff0c;提供计算流体力学及波浪模型。软件采用高效并行化设计&#xff0c;可以在大规模处理器…...

堡垒机和跳板机之区别(The Difference between Fortress and Springboard Aircraft)

堡垒机和跳板机之区别 在网络安全、安全运维领域&#xff0c;堡垒机和跳板机是两个常被提及且功能相似的概念&#xff0c;但它们在实际应用、功能定位以及技术实现上存在着明显的差异。本文将对堡垒机和跳板机进行详细的解析与比较&#xff0c;帮助读者更好地理解这两种网络安…...

《Android 应用开发基础教程》——第五章:RecyclerView 列表视图与适配器机制

目录 第五章&#xff1a;RecyclerView 列表视图与适配器机制 5.1 为什么要使用 RecyclerView&#xff1f; 5.2 基本结构图 5.3 RecyclerView 使用步骤 1️⃣ 添加 RecyclerView 依赖&#xff08;Android Studio&#xff09; 2️⃣ 布局文件&#xff08;activity_main.xml&…...

【第四章】19-匹配规则定义

在优化Web服务器性能与增强其功能性的过程中,深入理解Nginx的location匹配规则显得尤为关键。它不仅决定了如何高效地路由不同类型的客户端请求,而且是实现精准响应的基础。通过掌握精确匹配、前缀匹配及正则表达式匹配等规则,管理员能够灵活配置以支持复杂的业务需求,同时…...

[PTA]2025CCCC-GPLT天梯赛 现代战争

来源&#xff1a;L1-112 现代战争-Pintia题意&#xff1a;给定 n m n\times m nm 的矩阵&#xff0c;进行 k k k 次操作&#xff0c;每次操作清除矩阵当前最大值所在行和所在列的全部元素&#xff0c;求最终矩阵。关键词&#xff1a;模拟(签到)题解&#xff1a;非常水的模拟…...

操作系统期中复习

未完待续----后续补充全书完整板 一、计算机系统概述 1.1操作系统的基本概念 1.1.1操作系统的概念 操作系统&#xff1a;是指控制和管理整个计算机系统的硬件与软件资源&#xff0c;合理地组织、调度计算机的工作与资源的分配&#xff0c;进而为用户和其他软件提供方便接口…...

Linux 入门十一:Linux 网络编程

一、概述 1. 网络编程基础 网络编程是通过网络应用编程接口&#xff08;API&#xff09;编写程序&#xff0c;实现不同主机上进程间的信息交互。它解决的核心问题是&#xff1a;如何让不同主机上的程序进行通信。 2. 网络模型&#xff1a;从 OSI 到 TCP/IP OSI 七层模型&…...

车载软件架构 --- 二级boot设计说明需求规范

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...

在Ubuntu下用Chrony做主从机时间同步

主机 下载chrony sudo apt install chrony修改配置文件&#xff1a; sudo gedit /etc/chrony/chrony.conf# Welcome to the chrony configuration file. See chrony.conf(5) for more # information about usuable directives.# This will use (up to): # - 4 sources fro…...

开箱即用:一款带世界时钟简约好用在线时间戳转换工具源码

这款工具简直是为“时间管理大师”和“国际化玩家”量身定制!它不仅支持全球十大热门语言,还能无缝切换多时区,帮你轻松搞定时间戳和日期的转换。重点是,它完全前端实现,无需复杂后端,部署起来比泡杯咖啡还简单!开发人员可以在本地电脑运行来进行时间戳装换,还可以加Ad…...

代码随想录第22天:回溯算法4

一、全排列&#xff08;Leetcode 46&#xff09; 与组合问题不同&#xff0c;排列问题要注意2个特点&#xff1a; 每层都是从0开始搜索而不是startIndex需要used数组记录path里都放了哪些元素 class Solution:def permute(self, nums):result [] # 存储所有的排列self.back…...

cdq 系列 题解

从二维数点&#xff08;二维偏序&#xff09;到三维偏序。 用 cdq 分治可以解决二维数点问题。 1.洛谷 P1908 逆序对 题意 求所有数对 ( i , j ) (i,j) (i,j) 的个数&#xff0c;满足 i < j i<j i<j 且 a i > a j a_i>a_j ai​>aj​。 1 ≤ n ≤ 5 1…...

稳压二极管详解:原理、作用、应用与选型要点

一、稳压二极管的基本定义 稳压二极管&#xff08;齐纳二极管&#xff0c;Zener Diode&#xff09; 是一种利用反向击穿特性实现电压稳定的半导体器件。其核心特性是&#xff1a;在反向击穿时&#xff0c;两端电压几乎恒定&#xff08;Vz&#xff09;&#xff0c;且不会因电流…...

如何在量子计算时代保障 Sui 的安全性

量子计算的出现对依赖加密机制的系统构成了重大威胁。区块链依赖加密技术来进行身份管理、安全交易和数据完整性保护&#xff0c;而量子计算具备打破传统加密模型的能力&#xff0c;因此区块链面临特别严峻的挑战。 然而&#xff0c;Sui 天生具备“加密灵活性”&#xff0c;可…...

linux sysfs使用cat无显示的原因:返回值未赋值

在Linux驱动中通过sysfs定义的文件使用cat命令无显示&#xff0c;通常由以下原因导致&#xff1a; 1. show函数未正确实现 原因&#xff1a;show函数&#xff08;如show_status&#xff09;未正确填充缓冲区或返回有效字节数。 排查&#xff1a; // 错误示例&#xff1a;未写…...

Discuz论坛网站忘记管理员密码进不去管理中心怎么办?怎么改管理员密码?

Discuz论坛网站忘记管理员密码进不去管理中心怎么办&#xff1f;怎么改管理员密码&#xff1f;今天驰网飞飞和你分享 首先我们需要用到Discuz&#xff01;急诊箱tools.php这个文件&#xff0c;可在下载中心搜索关键词下载&#xff0c;下载好后将tools.php文件放到网站根目录&a…...

基于LangChain与Neo4j构建企业关系图谱的金融风控实施方案,结合工商数据、供应链记录及舆情数据,实现隐性关联识别与动态风险评估

以下是基于LangChain与Neo4j构建企业关系图谱的金融风控实施方案&#xff0c;结合工商数据、供应链记录及舆情数据&#xff0c;实现隐性关联识别与动态风险评估&#xff1a; 一、数据整合与图谱构建 多源数据融合与清洗 • 数据源&#xff1a;整合企业工商数据&#xff08;股权…...

数据结构第六章(五)-拓扑排序、关键路径

数据结构第六章&#xff08;五&#xff09; 图的应用&#xff08;二&#xff09;一、有向无环图二、拓扑排序1. AOV网2. 拓扑排序3. 逆拓扑排序 三、关键路径1.AOE网2.关键路径2.1 介绍2.2 关键路径的求法 总结 图的应用&#xff08;二&#xff09; 一、有向无环图 首先我们得…...

stc32单片机实现串口2M波特率满带宽传输

我需要实现已极高的速度用串口往上位机发送数据, 并且还不能占用mcu资源, 使用的单片机位stc32g8K64 我的方法是串口接收采用中断接收, 发送采用dma自动发送, 预先初始化16个64字节的缓冲区, 每次通过串口发送时, 先找到当前的空闲缓冲区, 然后往缓冲区里填充数据, 在dma传输完…...

uni-app 状态管理深度解析:Vuex 与全局方案实战指南

uni-app 状态管理深度解析&#xff1a;Vuex 与全局方案实战指南 一、Vuex 使用示例 1. 基础 Vuex 配置 1.1 项目结构 src/ ├── store/ │ ├── index.js # 主入口文件 │ └── modules/ │ └── counter.js # 计数器模块 └── main.js …...

STM32之DHT11温湿度传感器---附代码

DHT11简介 DHT11的供电电压为 3&#xff0d;5.5V。 传感器上电后&#xff0c;要等待 1s 以越过不稳定状态在此期间无需发送任何指令。 电源引脚&#xff08;VDD&#xff0c;GND&#xff09;之间可增加一个100nF 的电容&#xff0c;用以去耦滤波。 DATA 用于微处理器与DHT11之间…...