【C++航海王:追寻罗杰的编程之路】string类

目录

1 -> 为什么学习string类?

1.1 -> C语言中的字符串

2 -> 标准库中的string类

2.1 -> string类

2.2 -> string类的常用接口

3 -> string类的模拟实现

3.1 -> 经典的string类问题

3.2 -> 浅拷贝 

3.3 -> 深拷贝

3.3.1 -> 传统写法的string类

3.3.2 -> 现代写法的string类

3.4 -> 写时拷贝

3.5 -> string类的模拟实现


1 -> 为什么学习string类?

1.1 -> C语言中的字符串

在C语言中,字符串是以‘\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串时分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

OOP ->  Object Oriented Programming(面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。

核心思想:封装、继承、多态。

使用OOP的好处:

  • 易维护
  • 质量高
  • 效率高
  • 易扩展
并且在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

2 -> 标准库中的string类

2.1 -> string类

  1. 字符串是表示字符序列的类;
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性;
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型);
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数;
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类;
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作;
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
  4. 不能操作多字节或者变长字符的序列。

在使用string类时,必须包括#include头文件以及using namespace std;

2.2 -> string类的常用接口

1. string类对象的常见构造

 

函数名称功能说明
string()
构造空的string类对象,即空字符串
string(const char* s)
用C-string来构造string类对象
string(size_t n, char c)
string类对象中包含n个字符c
string(const string& s)
拷贝构造函数
#include <iostream>
using namespace std;void Teststring()
{string s1;				// 构造空的string类对象s1string s2("hello bit"); // 用C格式字符串构造string类对象s2string s3(s2);			// 拷贝构造s3
}int main()
{return 0;
}

2. string类对象的容量操作

函数名称功能说明
size

返回字符串有效字符长度

length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串释放为空串,是返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成n个,多出空间用字符c填充
#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <string>
using namespace std;// 测试string容量相关的接口
// size/clear/resize
void Teststring1()
{// 注意:string类对象支持直接用cin和cout进行输入和输出string s("hello, world");cout << s.size() << endl;cout << s.length() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小s.clear();cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到10个,多出位置用'a'进行填充// “aaaaaaaaaa”s.resize(10, 'a');cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充// "aaaaaaaaaa\0\0\0\0\0"// 注意此时s中有效字符个数已经增加到15个s.resize(15);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中有效字符个数缩小到5个s.resize(5);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;
}//====================================================================================
void Teststring2()
{string s;// 测试reserve是否会改变string中有效元素个数s.reserve(100);cout << s.size() << endl;cout << s.capacity() << endl;// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小s.reserve(50);cout << s.size() << endl;cout << s.capacity() << endl;
}// 利用reserve提高插入数据的效率,避免增容带来的开销
//====================================================================================
void TestPushBack()
{string s;size_t sz = s.capacity();cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}// 构建vector时,如果提前已经知道string中大概要放多少个元素,可以提前将string中空间设置好
void TestPushBackReserve()
{string s;s.reserve(100);size_t sz = s.capacity();cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}int main()
{return 0;
}

注意:

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size();
  2. clear()只是将string中有效字符清空,不改变底层空间大小;
  3. resize(size_t n)与resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变;
  4. reserve(size_t res_arg = 0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。

3. string类对象的访问及遍历操作 

 函数名称功能说明
operator[]返回pos位置的字符,const string类对象调用
begin + endhuoqbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rendhuoqbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式
#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <string>
using namespace std;// string的遍历
// begin()+end()   for+[]  范围for
// 注意:string遍历时使用最多的还是for+下标 或者 范围for(C++11后才支持)
// begin()+end()大多数使用在需要使用STL提供的算法操作string时,比如:采用reverse逆置string
void Teststring3()
{string s1("hello world");const string s2("Hello world");cout << s1 << " " << s2 << endl;cout << s1[0] << " " << s2[0] << endl;s1[0] = 'H';cout << s1 << endl;// s2[0] = 'h';   代码编译失败,因为const类型对象不能修改
}void Teststring4()
{string s("hello world");// 3种遍历方式:// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,// 另外以下三种方式对于string而言,第一种使用最多// 1. for+operator[]for (size_t i = 0; i < s.size(); ++i)cout << s[i] << endl;// 2.迭代器string::iterator it = s.begin();while (it != s.end()){cout << *it << endl;++it;}// string::reverse_iterator rit = s.rbegin();// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型auto rit = s.rbegin();while (rit != s.rend())cout << *rit << endl;// 3.范围forfor (auto ch : s)cout << ch << endl;
}int main()
{return 0;
}

4. string类对象的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回c格式字符串
find + npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回
#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <string>
using namespace std;// 测试string:
// 1. 插入(拼接)方式:push_back  append  operator+= 
// 2. 正向和反向查找:find() + rfind()
// 3. 截取子串:substr()
// 4. 删除:erase
void Teststring5()
{string str;str.push_back(' ');   // 在str后插入空格str.append("hello");  // 在str后追加一个字符"hello"str += 'b';           // 在str后追加一个字符'b'   str += "it";          // 在str后追加一个字符串"it"cout << str << endl;cout << str.c_str() << endl;   // 以C语言的方式打印字符串// 获取file的后缀string file("string.cpp");size_t pos = file.rfind('.');string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;// npos是string里面的一个静态成员变量// static const size_t npos = -1;// 取出url中的域名string url("http://www.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");if (start == string::npos){cout << "invalid url" << endl;return;}start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;// 删除url的协议前缀pos = url.find("://");url.erase(0, pos + 3);cout << url << endl;
}int main()
{return 0;
}

注意:

  1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串;
  2. 对sting操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

5. string类非成员函数

函数功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>输入运算符重载
operator<<输出运算符重载
getline获取一行字符串
relational operators大小比较

6. vs和g++下string结构的说明

注意:下述结构是在32位平台下进行,32位平台下指针占4个字节。

  • vs下string的结构

string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放;
  • 当字符串长度大于等于16时,从堆上开辟空间。
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量。

最后:还有一个指针做一些其他事情。

故总共占16 + 4 + 4 + 4 = 28个字节。

  • g++下string的结构

g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
struct _Rep_base
{size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount;
};
  • 指向堆空间的指针,用来存储字符串

3 -> string类的模拟实现

3.1 -> 经典的string类问题

模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <string>
#include <cassert>
using namespace std;class String
{
public:String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}~String(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;
};void TestString()
{String s1("hello world");String s2(s1);
}

 

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝

3.2 -> 浅拷贝 

浅拷贝:

也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生访问违规。

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

3.3 -> 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数,赋值运算符重载以及构造函数必须要显式给出。一般情况都是按照深拷贝方式提供。

3.3.1 -> 传统写法的string类

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <string>
#include <cassert>
using namespace std;class String
{
public:String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}String& operator=(const String& s){if (this != &s){char* pStr = new char[strlen(s._str) + 1];strcpy(pStr, s._str);delete[] _str;_str = pStr;}return *this;}~String(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;
};

3.3.2 -> 现代写法的string类

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <string>
#include <cassert>
using namespace std;class String
{
public:String(const char* str = ""){if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s): _str(nullptr){String strTmp(s._str);swap(_str, strTmp._str);}String& operator=(String s){swap(_str, s._str);return *this;}~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};

3.4 -> 写时拷贝

写时拷贝就是一种拖延症,实在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象是资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

3.5 -> string类的模拟实现

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <assert.h>
using namespace std;namespace fyd
{class string{public:typedef char* iterator;public:string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}string(const string& s): _str(nullptr), _size(0), _capacity(0){string tmp(s._str);this->swap(tmp);}string& operator=(string s){this->swap(s);return *this;}~string(){if (_str){delete[] _str;_str = nullptr;}}iterator begin(){return _str;}iterator end(){return _str + _size;}void push_back(char c){if (_size == _capacity)reserve(_capacity * 2);_str[_size++] = c;_str[_size] = '\0';}string& operator+=(char c){push_back(c);return *this;}void append(const char* str){size_t len = strlen(str);if ((_size + len) > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}string& operator+=(const char* str){append(str);return *this;}void clear(){_size = 0;_str[_size] = '\0';}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}const char* c_str()const{return _str;}size_t size()const{return _size;}size_t capacity()const{return _capacity;}bool empty()const{return 0 == _size;}void resize(size_t newSize, char c = '\0'){if (newSize > _size){// 如果newSize大于底层空间大小,则需要重新开辟空间if (newSize > _capacity){reserve(newSize);}memset(_str + _size, c, newSize - _size);}_size = newSize;_str[newSize] = '\0';}void reserve(size_t newCapacity){// 如果新容量大于旧容量,则开辟空间if (newCapacity > _capacity){char* str = new char[newCapacity + 1];strcpy(str, _str);// 释放原来旧空间,然后使用新空间delete[] _str;_str = str;_capacity = newCapacity;}}// accesschar& operator[](size_t index){assert(index < _size);return _str[index];}const char& operator[](size_t index)const{assert(index < _size);return _str[index];}bool operator<(const string& s){return strcmp(_str, s._str) < 0;}bool operator==(const string& s){return strcmp(_str, s._str) == 0;}bool operator<=(const string& s){return ((_str < s._str) || (_str == s._str));}bool operator>(const string& s){return !((_str < s._str) || (_str == s._str));}bool operator>=(const string& s){return !(strcmp(_str, s._str) < 0);}bool operator!=(const string& s){return !(strcmp(_str, s._str) == 0);}// 返回c在string中第一次出现的位置size_t find(char c, size_t pos = 0) const{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return -1;}// 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos = 0) const{const char* ret = strstr(_str + pos, s);if (ret)return ret - _str;return -1;}// 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}char* end = _str + _size;while (end > _str + pos){*(end + 1) = *end;--end;}_str[pos] = c;_size++;return *this;}string& insert(size_t pos, const char* str){size_t len = strlen(str);if ((_size + len) > _capacity){reserve(_size + len);}char* end = _str + _size;while (end > (_str + pos)){*(end + len) = *end;--end;}strncpy(_str + pos, str, len);_size += len;return *this;}// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len){size_t leftNum = _size - pos;if (len >= leftNum){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}private:friend ostream& operator<<(ostream& _cout, const fyd::string& s);friend istream& operator>>(istream& _cin, fyd::string& s);private:char* _str;size_t _capacity;size_t _size;};ostream& operator<<(ostream& _cout, const fyd::string& s){for (size_t i = 0; i < s.size(); ++i){_cout << s[i];}return _cout;}istream& operator>>(istream& _cin, string& s){s.clear();char ch;ch = _cin.get();while (ch != ' ' && ch != '\n'){s += ch;ch = _cin.get();}return _cin;}
}///对自定义的string类进行测试
void TestString()
{fyd::string s1("hello");s1.push_back(' ');s1.push_back('w');s1.push_back('o');s1.push_back('r');s1.push_back('l');s1 += 'd';cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;// 利用迭代器打印string中的元素fyd::string::iterator it = s1.begin();while (it != s1.end()){cout << *it;++it;}cout << endl;// 这里可以看到一个类只要支持的基本的iterator,就支持范围forfor (auto ch : s1)cout << ch;cout << endl;
}int main()
{TestString();return 0;
}


感谢大佬们的支持!!!

互三啦!!!

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

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

相关文章

【大厂AI课学习笔记】【2.1 人工智能项目开发规划与目标】(5)数据管理

今天学习了数据管理&#xff0c;以及数据管理和数据治理的区别和联系。 数据管理&#xff1a;利用计算机硬件和软件技术对数据进行有效的收集、存储、处理和应用的过程其目的在于充分有效地发挥数据的作用。 实现数据有效管理的关键是数据组织。 数据管理和数据治理的区别&am…

《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)

文章目录 9.1 连接数据库 - Go 语言的海底宝藏之门9.1.1 基础知识讲解安装数据库驱动数据库连接 9.1.2 重点案例&#xff1a;用户信息管理系统准备数据库Go 代码实现连接数据库添加新用户查询用户信息用户登录验证主函数 9.1.3 拓展案例 1&#xff1a;批量添加用户准备数据库Go…

mpack简明教程

文章目录 摘要MessagePack简介MPACK的简单使用在定长的buffer存储不定长的数据读取截断的数据 摘要 本文先简单介绍MessagePack的基本概念。 然后&#xff0c;介绍一个MessagePack C API - MPack的通常使用。 接着尝试对MPack截断数据的读取。 注&#xff1a;本文完整代码见…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱9(附带项目源码)

效果演示 文章目录 效果演示系列目录前言箱子库存源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中&#xff0c;我们将探索如何用unity制作一个3D背包、库存、制作、快捷栏、存…

信号系统之卷积性质

1 常见的脉冲响应 最简单的脉冲响应是一个δ函数&#xff0c;如图7-1所示。也就是说&#xff0c;输入上的脉冲在输出上产生相同的脉冲。这意味着所有信号都毫无变化地通过系统。将任何信号与 δ函数进行卷积都会产生完全相同的信号。从数学上来说&#xff0c;可以这样写&#…

元器件焊盘的PCB处理方式分析与总结

对于高速信号走线的特性阻抗&#xff0c;都需要按照实际要求进行精度控制&#xff0c;所以&#xff0c;任何因设计因素带来的阻抗波动都应该进行优化&#xff0c;如下图所示&#xff0c;为一个12层板设计中的50Ω微带走线&#xff0c;需要在走线之上放置电感&#xff1b; 但是&…

枚举(C/C++)

没有什么成套的算法&#xff0c;直接上例题&#xff01;&#xff01; 例题1&#xff1a;赢球票 代码&#xff1a; #include <bits/stdc.h> using namespace std;const int maxn 105; int n,num1[maxn],num2[maxn],cnt,cnt1,sum,ans;int check1()//检查剩余个数 {cnt1…

Guava RateLimiter单机实战指南

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Guava RateLimiter单机实战指南 前言maven坐标引入业务实现重要参数和方法关于warmupPeriod实战 前言 想象一下你是一位大厨&#xff0c;正在烹饪美味佳肴。突然之间&#xff0c;前来就餐的人潮如潮水…

得物面试:Redis用哈希槽,而不是一致性哈希,为什么?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; Redis为何用哈希槽而不用一致性哈希&#xff1f; 最近…

WebServer 之 http连接处理(下)

目录 ✊请求报文--解析 流程图 && 状态机 状态机 -- 状态转移图 主状态机 从状态机 http 报文解析 HTTP_CODE 含义 从状态机 逻辑 主状态机 逻辑 &#x1f41e;请求报文--响应 基础API stat mmap iovec writev 流程图 HTTP_CODE 含义(2) 代码分析 …

图表示学习 Graph Representation Learning chapter2 背景知识和传统方法

图表示学习 Graph Representation Learning chapter2 背景知识和传统方法 2.1 图统计和核方法2.1.1 节点层次的统计和特征节点的度 节点中心度聚类系数Closed Triangles, Ego Graphs, and Motifs 图层次的特征和图的核节点袋Weisfieler–Lehman核Graphlets和基于路径的方法 邻域…

qt-C++笔记之捕获鼠标滚轮事件并输出滚轮角度增量

qt-C笔记之捕获鼠标滚轮事件并输出滚轮角度增量 code review! 文章目录 qt-C笔记之捕获鼠标滚轮事件并输出滚轮角度增量1.运行2.main.cpp3.main.pro 1.运行 2.main.cpp #include <QApplication> #include <QWidget> #include <QWheelEvent> #include <…

Android 回退页面不是上个页面

问题 Android 回退页面不是上个页面 详细问题 笔者进行Android 开发&#xff0c;点击返回上一层&#xff0c;显示页面不是上个页面&#xff0c;而是之前的某个页面 页面跳转代码 private void navigateToActivity(Context context, Class<?> targetActivityClass) {I…

C#,二分法(Bisection Method)求解方程的算法与源代码

1 二分法 二分法是一种分治算法&#xff0c;是一种数学思维。 对于区间[a&#xff0c;b]上连续不断且f&#xff08;a&#xff09;f&#xff08;b&#xff09;<0的函数yf&#xff08;x&#xff09;&#xff0c;通过不断地把函数f&#xff08;x&#xff09;的零点所在的区间…

springboot登录校验

一、登录功能 二、登录校验 2.1 会话技术 2.2 JWT令牌 JWT令牌解析&#xff1a; 如何校验JWT令牌&#xff1f;Filter和Interceptor两种方式。 2.3 过滤器Filter 2.3.1 快速入门 修改上述代码&#xff1a; 2.3.2 详解 2.3.3 登录校验-Filter 2.4 Interceptor拦截器 2.4.1 …

量子算法入门——3.狄拉克符号与量子态(1)

参考资料&#xff1a; 【【零基础入门量子计算-第04讲】狄拉克符号与量子态】 来自b站up&#xff1a;溴锑锑跃迁 建议关注他的更多高质量文章&#xff1a;CSDN&#xff1a;【溴锑锑跃迁】 1. 狄拉克符号 从生活实例引导到狄拉克符号狄拉克符号 注意这里ket是| >(右矢)&a…

阿里云服务器租用价格 2024年新版活动报价及租用收费标准

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

【力扣】169.多数元素

这道题的解法是运用哈希表打擂台的思想 首先题目的意思是存在数字&#xff0c;意思就是最后返回的结果不可能为空就是了&#xff0c;所以便不用考虑{1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5}这种例子。那么就可以用哈希表存所出现数字出现的次数&#xff0c;然…

【前端工程化面试题】webpack proxy的工作原理,为什么能解决跨域问题

在 webpack 的配置文件 webpack.config.js 中有一个配置项 devServer 里面有一个属性是 proxy&#xff0c;这里面可以配置代理服务器&#xff0c;解决跨域问题&#xff0c;请参考官网。 一般来说 webpack 的代理就是说的开发服务器 webpack-dev-server。 其实不光是 webpack 其…

恢复被.target勒索病毒加密的数据文件:拒绝向.target勒索病毒支付赎金

引言&#xff1a; 在当今数字时代&#xff0c;勒索病毒已成为网络安全领域的一大威胁&#xff0c;而.target勒索病毒是其中引起广泛关注的一种变种。本文将深入探讨.target勒索病毒的特点以及被其加密的数据文件恢复方法。数据的重要性不容小觑&#xff0c;您可添加我们的技术…