【C++】C++中的String类详解及模拟实现示例

文章目录

  • string类简介
    • string类的基本用法
    • string类的常用方法
    • string类的优势
  • string类的模拟实现
    • 存储结构
    • 头文件string.h
    • 源文件string.cpp
    • 源文件test.cpp

string类简介

string类简介在C++编程中,字符串是一种非常常见的数据类型,用于存储文本信息。C++标准库提供了string类来处理字符串,它提供了许多方法和功能,使得字符串操作更加方便和高效。

string类的基本用法

要使用C++中的string类,需要包含头文件。下面是一个简单的示例,演示了如何创建和操作string对象:

#include <iostream>
#include <string>int main() {// 创建一个空的string对象std::string str;// 初始化string对象std::string greeting = "Hello, world!";// 获取字符串长度int len = greeting.length();// 字符串拼接std::string message = greeting + " Have a nice day!";// 输出字符串std::cout << message << std::endl;return 0;
}

string类的常用方法

string类提供了丰富的方法来操作字符串,包括查找子串、比较字符串、截取子串等功能。下面是一些常用的方法示例:

std::string str = "This is a C++ string";// 查找子串
size_t found = str.find("C++");// 比较字符串
if (str.compare("Another string") == 0) {std::cout << "Strings are equal" << std::endl;
}// 截取子串
std::string sub = str.substr(8, 2);  // 从索引8开始,截取2个字符

string类的优势

与C风格字符串相比,C++的string类具有许多优势。它自动管理内存,避免了内存泄漏和越界访问的问题;提供了丰富的方法和操作符重载,使得字符串操作更加方便和直观;而且在C++11标准中,string类还支持移动语义,进一步提高了性能。

总的来说,C++中的string类是一个强大而灵活的工具,用于处理字符串数据。无论是简单的字符串拼接,还是复杂的字符串处理,string类都能够满足需求并提供高效的解决方案。

string类的模拟实现

除了对string类的简单使用外,为了更好的了解string类的实现原理,我们可以试着实现string类的部分功能,以便我们可以更加深入了string的原理,以便可以更好的使用它。

存储结构

string本质上是一个char类型的顺序表,实现起来与顺序表相似,所以结构上也相似。

using namespace std;
#include <iostream>
#include <assert.h>// 将自定义字符串类封装在命名空间hd内
namespace hd {// 定义string类class string {public:// 成员常量const static size_t npos = -1; // 表示未找到的位置,它是无符号整型的最大值,//特殊用法,这个值给初始化列表,但是它又不走初始化列表//const static double x = 1.1; 这种特使用法只支持整数,像浮点数这些类型并不支持//这种特殊处理还有一个用法://const static int N = 10;//int a[N];private:// 私有数据成员size_t _size = 0; // 字符串大小size_t _capacity = 0; // 字符串容量char* _str = nullptr; // 指向动态分配的数组};
}

头文件string.h

// string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1 // 禁用安全警告,与Visual Studio相关
using namespace std;
#include <iostream>
#include <assert.h>// 将自定义字符串类封装在命名空间hd内
namespace hd {// 定义string类class string {public:// 构造函数、复制构造函数、赋值运算符和析构函数string(const char* str = ""); // 默认构造函数,可接受C风格字符串string(const string& s); // 复制构造函数string(const char* s, size_t n); // 从C风格字符串构造,并指定长度string& operator= (string s); // 赋值运算符,使用拷贝交换技术~string(); // 析构函数,释放内存// 迭代器相关typedef char* iterator; // 迭代器类型定义typedef const char* const_iterator; // 常量迭代器类型定义iterator begin() const { return _str; } // 返回指向第一个字符的迭代器iterator end() const { return _str + _size; } // 返回指向最后一个字符之后的迭代器// 容量相关函数size_t size() const { return _size; } // 返回字符串的大小void resize(size_t n, char c = '\0'); // 改变字符串的大小,如果变大则填充c字符void reserve(size_t n = 0); // 预留空间,避免频繁分配内存void clear();// 清空字符串size_t capacity() const { return _capacity; } // 返回字符串对象的容量bool empty() const { return _size == 0; } // 判断字符串对象是否为空// 元素访问char& operator[] (size_t pos) { assert(pos <= _size); return _str[pos]; } // 下标运算符const char& operator[] (size_t pos) const { assert(pos <= _size); return _str[pos]; } // 下标运算符常量版本char& at(size_t pos) { assert(pos <= _size); return _str[pos]; } // 安全访问元素const char& at(size_t pos) const { assert(pos <= _size); return _str[pos]; } // 安全访问元素常量版本// 修改器string& operator+= (const string& s) { return append(s); } // 重载+=运算符,用于追加字符串string& operator+= (const char* str) { return append(str); } // 重载+=运算符,用于追加C风格字符串string& operator+= (char c) { return append(1, c); } // 重载+=运算符,用于追加字符string& append(const string& s); // 追加字符串string& append(const char* str); // 追加C风格字符串string& append(size_t n, char c); // 追加n个字符cstring& append(const string& s, size_t subpos, size_t sublen); // 追加字符串的子串void push_back(char c); // 在字符串末尾添加一个字符string& insert(size_t pos, const string& str); // 在指定位置插入字符串string& insert(size_t pos, const char* s); // 在指定位置插入C风格字符串string& insert(size_t pos, size_t n, char c); // 在指定位置插入n个字符cstring& erase(size_t pos = 0, size_t len = npos); // 删除从pos开始的len个字符void swap(string& s); // 交换两个字符串的内容void pop_back() { erase(_size-1, 1); } // 删除最后一个字符// 字符串操作const char* c_str() const { return _str; } // 返回C风格字符串size_t copy(char* s, size_t len, size_t pos = 0); // 复制字符串到ssize_t find(const string& str, size_t pos = 0) const; // 查找子字符串size_t find(const char* s, size_t pos = 0) const; // 查找C风格子字符串size_t find(char c, size_t pos = 0) const; // 查找字符string substr(size_t pos = 0, size_t len = npos) const; // 返回子串//+号运算符重载friend string operator+(const string& lhs, const string& rhs);// 成员常量const static size_t npos = -1; // 表示未找到的位置private:// 私有数据成员size_t _size = 0; // 字符串大小size_t _capacity = 0; // 字符串容量char* _str = nullptr; // 指向动态分配的数组};// 非成员函数重载istream& operator>> (istream& is, string& str); // 输入运算符重载ostream& operator<< (ostream& os, const string& str); // 输出运算符重载istream& getline(istream& is, string& str, char delim); // 从输入流读取一行istream& getline(istream& is, string& str); // 从输入流读取一行,直到换行符
}};// 非成员函数重载istream& operator>> (istream& is, string& str); // 输入运算符重载ostream& operator<< (ostream& os, const string& str); // 输出运算符重载istream& getline(istream& is, string& str, char delim); // 从输入流读取一行istream& getline(istream& is, string& str); // 从输入流读取一行,直到换行符
}

源文件string.cpp


#include "string.h"namespace hd
{// 构造函数:根据传入的C风格字符串构造字符串对象string::string(const char* str){_size = strlen(str);  // 计算字符串长度_capacity = _size;  // 初始容量为字符串长度_str = new char[_capacity + 1];  // 分配内存空间,+1是为了存放字符串结束符'\0'strcpy(_str, str);  // 将传入的字符串拷贝到_str中}// 拷贝构造函数:使用另一个字符串对象构造当前字符串对象string::string(const string& s){string tmp(s.c_str());  // 利用传入的字符串对象构造临时字符串对象swap(tmp);  // 交换当前字符串对象和临时字符串对象的成员变量值}// 构造函数:根据传入的C风格字符串和长度构造字符串对象string::string(const char* s, size_t n) {string tmp(s);  // 利用传入的C风格字符串构造临时字符串对象tmp[n] = '\0';  // 限制临时字符串对象的长度为ntmp._size = n;  // 更新临时字符串对象的大小为nswap(tmp);  // 交换当前字符串对象和临时字符串对象的成员变量值}// 赋值运算符重载:将字符串对象s赋值给当前字符串对象string& string::operator=(string s){swap(s);  // 交换当前字符串对象和s的成员变量值return *this;  // 返回当前字符串对象}// 析构函数:释放字符串对象所占用的内存空间string::~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}// 修改字符串大小,使其包含n个字符,如果n小于当前大小,则截断字符串,如果n大于当前大小,则在末尾填充字符cvoid string::resize(size_t n, char c){if (this->size() > n) {_str[n] = '\0';  // 截断字符串_size = n;}else {reserve(n);  // 扩容字符串,使其能够容纳n个字符append(n - this->size(), c);  // 在末尾填充字符c,直到达到长度n}}// 扩展字符串容量,以容纳至少n个字符void string::reserve(size_t n){if (n > _capacity) {char* tmp = new char[n + 1];  // 分配新的内存空间,+1是为了存放字符串结束符'\0'strcpy(tmp, _str);  // 将原字符串拷贝到新的内存空间中delete[] _str;  // 释放原有内存空间_str = tmp;  // 更新字符串指针指向新的内存空间_capacity = n;  // 更新字符串容量}}// 在字符串末尾追加另一个字符串对象string& string::append(const string& s){return append(s.c_str());}// 在字符串末尾追加C风格字符串string& string::append(const char* str){size_t len = strlen(str);  // 计算要追加的字符串长度if (_size + len > _capacity) {  // 如果追加后的长度大于当前容量,则扩容reserve(_size + len);}strcpy(_str + _size, str);  // 将要追加的字符串拷贝到当前字符串的末尾_size += len;  // 更新字符串大小return *this;}// 在字符串末尾追加n个字符cstring& string::append(size_t n, char c){for (size_t i = 0; i < n; ++i) {this->push_back(c);}return *this;}// 在字符串末尾追加另一个字符串对象的子串,子串起始位置为subpos,长度为sublenstring& string::append(const string& s, size_t subpos, size_t sublen){size_t len = s.size();  // 获取字符串的大小if (_size + len > _capacity) {  // 如果追加后的长度大于当前容量,则扩容reserve(_size + len);}const char* cp_str = s.c_str() + subpos;  // 根据起始位置计算子串的指针strncpy(_str + _size, cp_str, sublen);  // 将子串拷贝到当前字符串的末尾_size += sublen;  // 更新字符串大小_str[_size] = '\0';  // 添加字符串结束符return *this;}// 在字符串末尾添加一个字符cvoid string::push_back(char c){if (_size == _capacity) {  // 如果字符串已满,则进行扩容size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = c;  // 在末尾添加字符c++_size;  // 更新字符串大小_str[_size] = '\0';  // 添加字符串结束符}// 在字符串的指定位置插入另一个字符串对象string& string::insert(size_t pos, const string& str){assert(pos <= _size);  // 确保插入位置在合法范围内return this->insert(pos, str.c_str());}// 在字符串的指定位置插入C风格字符串string& string::insert(size_t pos, const char* s){assert(pos <= _size);  // 确保插入位置在合法范围内size_t len = strlen(s);  // 计算要插入的字符串长度if (_size + len > _capacity) {  // 如果插入后的长度大于当前容量,则扩容reserve(_size + len);}memmove(_str + pos + len, _str + pos, _size - pos);  // 将插入位置及之后的字符后移memcpy(_str + pos, s, len);  // 将要插入的字符串拷贝到指定位置_size += len;  // 更新字符串大小_str[_size] = '\0';  // 添加字符串结束符return *this;}// 在字符串的指定位置插入n个字符cstring& string::insert(size_t pos, size_t n, char c){assert(pos <= _size);  // 确保插入位置在合法范围内string str;while (n--) {str += c;}return this->insert(pos, str);}// 删除字符串中从指定位置开始的指定长度的字符string& string::erase(size_t pos, size_t len){if (len == npos || pos + len >= _size) {  // 如果要删除的长度超过了字符串的长度,则截断字符串_str[pos] = '\0';_size = pos;return *this;}else {strcpy(_str + pos, _str + pos + len);  // 将要删除的部分后面的字符前移_size -= len;  // 更新字符串大小}return *this;}// 交换两个字符串对象的成员变量值void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 将指定长度的字符从字符串中复制到字符数组s中,返回实际复制的字符数size_t string::copy(char* s, size_t len, size_t pos) {assert(pos <= _size);  // 确保复制位置在合法范围内size_t actual_len = min(len, _size - pos);  // 计算实际要复制的字符数strncpy(s, _str + pos, actual_len);  // 将要复制的字符拷贝到数组中return actual_len;}// 查找字符串中指定子串的起始位置,从指定的位置pos开始查找size_t string::find(const string& str, size_t pos) const{assert(pos <= _size);  // 确保查找位置在合法范围内return find(str.c_str(), pos);}// 查找字符串中指定C风格子串的起始位置,从指定的位置pos开始查找size_t string::find(const char* s, size_t pos) const{assert(pos <= _size);  // 确保查找位置在合法范围内char* ptr = strstr(_str + pos, s);  // 在字符串中查找子串if (ptr == nullptr) {return npos;  // 如果找不到子串,则返回特殊值 npos,表示找不到}else {return ptr - _str;  // 如果找到子串,则返回子串在字符串中的起始位置}}// 返回字符串对象的子串,从指定位置pos开始,长度为lenstring string::substr(size_t pos, size_t len) const{assert(pos <= _size);  // 确保起始位置在合法范围内size_t actual_len = min(len, _size - pos);  // 计算实际要复制的字符数return string(_str + pos, actual_len);  // 返回从起始位置pos开始,长度为actual_len的子串}// 清空字符串对象,将大小和容量都重置为0void string::clear(){delete[] _str;  // 释放之前分配的内存空间_str = new char[1];  // 分配新的内存空间,大小为1,用于存放字符串结束符'\0'_str[0] = '\0';  // 设置字符串结束符_size = 0;  // 更新字符串大小_capacity = 0;  // 更新字符串容量}string operator+(const string& lhs, const string& rhs){std::size_t newLen = lhs._size + rhs._size;  // 计算拼接后字符串的长度char* newData = new char[newLen + 1];  // 分配足够的内存来存储新的字符串,+1 用于存储字符串末尾的 '\0'strcpy(newData, lhs._str);  // 将左操作数字符串复制到新字符串的开头位置strcpy(newData + lhs._size, rhs._str);  // 将右操作数字符串复制到新字符串的左操作数字符串之后return string(newData);  // 返回一个包含新字符串的 string 对象}istream& operator>> (istream& is, string& str){return getline(is, str, ' ');  // 使用 getline 函数从 istream 中读取字符串,以空格作为分隔符}ostream& operator<< (ostream& os, const string& str){for (auto& ch : str){os << ch;  // 逐个字符地写入 ostream}return os;}istream& getline(istream& is, string& str, char delim){str.clear();  // 清空待填充的字符串char buffer[128];  // 临时缓冲区,为了减少扩容次数,用于存储每次从 istream 中读取的字符char ch = is.get();  // 从 istream 中获取一个字符//in >> ch;		//会导致一直连续输入无法结束//遇到空格或者换行时会被忽略掉,用get可以收到空格和换行的内容int i = 0;  // 缓冲区索引while (ch != '\n' && ch != delim)  // 依次读取字符,直到遇到换行符或指定分隔符{buffer[i++] = ch;if (i == 127) {  // 当临时缓冲区满时,将缓冲区的内容添加到字符串中buffer[i] = '\0';  // 将缓冲区末尾添加 '\0',形成 C 风格字符串str += buffer;  // 将缓冲区的内容追加到字符串中i = 0;  // 重置缓冲区索引}ch = is.get();  // 继续从 istream 中获取下一个字符}if (i > 0)  // 如果缓冲区中还有剩余的字符,则将它们添加到字符串中{buffer[i] = '\0';str += buffer;i = 0;}return is;  // 返回 istream 对象的引用,以支持链式操作}istream& getline(istream& is, string& str){str.clear();  // 清空待填充的字符串char buffer[128];  // 临时缓冲区,为了减少扩容次数,用于存储每次从 istream 中读取的字符char ch = is.get();  // 从 istream 中获取一个字符//in >> ch;		//会导致一直连续输入无法结束//遇到空格或者换行时会被忽略掉,用get可以收到空格和换行的内容int i = 0;  // 缓冲区索引while (ch != '\n')  // 依次读取字符,直到遇到换行符{buffer[i++] = ch;if (i == 127) {  // 当临时缓冲区满时,将缓冲区的内容添加到字符串中buffer[i] = '\0';  // 将缓冲区末尾添加 '\0',形成 C 风格字符串str += buffer;  // 将缓冲区的内容追加到字符串中i = 0;  // 重置缓冲区索引}ch = is.get();  // 继续从 istream 中获取下一个字符}if (i > 0)  // 如果缓冲区中还有剩余的字符,则将它们添加到字符串中{buffer[i] = '\0';str += buffer;i = 0;}return is;  // 返回 istream 对象的引用,以支持链式操作}
}

源文件test.cpp

//test.cpp
#include "string.h"int main() {hd::string s1; // 默认构造函数cout << "s1: " << s1 << endl;hd::string s2("Hello"); // 构造函数,接受C风格字符串cout << "s2: " << s2 << endl;hd::string s3(s2); // 复制构造函数cout << "s3: " << s3 << endl;hd::string s4("World", 3); // 构造函数,接受C风格字符串和长度cout << "s4: " << s4 << endl;// 迭代器测试cout << "s2: ";for (auto it = s2.begin(); it != s2.end(); ++it) {cout << *it;}cout << endl;// 容量相关函数测试cout << "s2 size: " << s2.size() << endl;cout << "s2 capacity: " << s2.capacity() << endl;s2.resize(10, 'a');cout << "s2 after resize: " << s2 << endl;cout << "s2 capacity after resize: " << s2.capacity() << endl;s2.reserve(20);cout << "s2 capacity after reserve: " << s2.capacity() << endl;s2.clear();cout << "s2 size after clear: " << s2.size() << endl;cout << "s2 capacity after clear: " << s2.capacity() << endl;cout << "s2 is empty: " << (s2.empty() ? "true" : "false") << endl;// 元素访问测试cout << "s3[0]: " << s3[0] << endl;cout << "s3.at(1): " << s3.at(1) << endl;// 修改器测试s3 += " World"; // 追加字符串cout << "s3 after append: " << s3 << endl;s3.insert(5, " C++"); // 在指定位置插入字符串cout << "s3 after insert: " << s3 << endl;s3.erase(5, 4); // 删除指定位置的字符cout << "s3 after erase: " << s3 << endl;s3.pop_back(); // 删除最后一个字符cout << "s3 after pop_back: " << s3 << endl;// 字符串操作测试const char* cStr = s3.c_str();cout << "C-style string: " << cStr << endl;char buffer[10];size_t copiedLen = s3.copy(buffer, 5, 0); // 复制字符串到bufferbuffer[copiedLen] = '\0';cout << "Copied string: " << buffer << endl;s3 += "World !";size_t pos = s3.find("World"); // 查找子字符串if (pos != hd::string::npos) {cout << "Position of 'World': " << pos << endl;}else {cout << "Position of 'World': " << -1 << endl;}hd::string subStr = s3.substr(6, 3); // 返回子串cout << "Substring: " << subStr << endl;return 0;
}

分别测试库中,和自己实现得出的结果:
这里的resize与reserve的不同,是因为在实现过程中的扩容策略的不同造成的。
在这里插入图片描述
通过全面介绍了C++中string类的基本用法、常用方法和优势,以及string类的模拟实现示例。通过详细的讲解和示例代码,可以深入了解如何在C++中使用string类进行字符串操作,并了解其相对于C风格字符串的优势所在。同时,通过展示一个简单的string类的模拟实现,还可以加深对string类内部原理的理解。

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

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

相关文章

我的网站服务器被入侵了该怎么办?

最近有用户咨询到德迅云安全&#xff0c;说自己再用的网站服务器遇到了入侵情况&#xff0c;询问该怎么处理入侵问题&#xff0c;有什么安全方案可以解决服务器被入侵的问题。下面&#xff0c;我们就来简单讲下服务器遇到入侵了&#xff0c;该从哪方面入手处理&#xff0c;在预…

全球化需要先搬离中国?中国公司出海不应失去“模式自信”

中国企业出海近期热闹非凡&#xff0c;其中以短剧为代表的文化内容产业和跨境电商产业都吸引了大量关注。例如亚马逊在12月12日公布一组最新数据&#xff0c;亚马逊过去一年销售额超过1000万美金的中国卖家数量&#xff0c;同比增长接近30%。中国跨境电商平台在刚刚过去的“黑五…

机器学习算法应用场景与评价指标

一、应用场景 机器学习的算法选择大部分依赖于具体的问题类型和数据特征。下面是一些典型的场景以及对应的常用算法&#xff1a; 1.二元分类问题 当你的目标变量只有两个类别时&#xff0c;如垃圾邮件过滤&#xff08;垃圾邮件/非垃圾邮件&#xff09;、患者疾病诊断&#x…

医院污水处理设备远程监控超标报警解决方案

行业背景 近年来&#xff0c;我国医疗机构建设得到了巨大的发展。根据《2022年我国卫生健康事业发展统计公报》&#xff0c;2022年末&#xff0c;全国医疗卫生机构总数达1032918个。截至2022年10月&#xff0c;根据全国排污许可证管理信息平台&#xff0c;共有 13316家医院核发…

UE引擎 LandscapeGrass 实现机制分析(UE5.2)

前言 随着电脑和手机硬件性能越来越高&#xff0c;游戏越来越追求大世界&#xff0c;而大世界非常核心的一环是植被&#xff0c;目前UE5引擎提供给植被生成的主流两种方式为 手刷植被和LandscapeGrass(WeightMap程序化植被)。当然UE5.3推出新一代PCGFramework 节点程序化生成框…

Halcon reduce_domain和scale_image的作用

在Halcon中&#xff0c;reduce_domain是用于缩小图像域&#xff08;Image Domain&#xff09;的操作。 它的作用是通过指定一个感兴趣区域&#xff08;ROI&#xff0c;Region of Interest&#xff09;&#xff0c;将图像数据限制在该区域内&#xff0c;从而实现对图像进行裁剪…

领导力的3个常见误区,你可能中了其中之一

什么是领导力&#xff1f; 领导力是组织和团队成功的关键。在一个不断变化的商业环境中&#xff0c;理解领导力的本质至关重要。这篇文章将揭示有关领导力的三个常见误解&#xff0c;帮助读者更清晰地认识领导者的角色。 关于领导力的常见误解 人们对领导力的理解经常受到错…

如何使用bash写脚本

本章主要介绍如何使用bash写脚本。 了解通配符了解变量了解返回值和数值运算数值的对比判断语句循环语句 grep的用法是“grep 关键字 file”&#xff0c;意思是从file中过滤出含有关键字的行。 例如&#xff0c;grep root /var/log/messages&#xff0c;意思是从/var/log/me…

在做题中学习(33):只出现一次的数字 II

137. 只出现一次的数字 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 1.首先想到出现三次的数&#xff0c;它们仨的任意一位都是相同的&#xff08;1/0&#xff09; 2.可以发现出现三次的数的某一位和a某一位在所有情况下%3最后的结果都和a的那一位相同&…

【PyTorch】卷积神经网络

文章目录 1. 理论介绍1.1. 从全连接层到卷积层1.1.1. 背景1.1.2. 从全连接层推导出卷积层 1.2. 卷积层1.2.1. 图像卷积1.2.2. 填充和步幅1.2.3. 多通道 1.3. 池化层&#xff08;又称汇聚层&#xff09;1.3.1. 背景1.3.2. 池化运算1.3.3. 填充和步幅1.3.4. 多通道 1.4. 卷积神经…

抖店商品卡流量怎么做?附实操玩法解析及案例分享!

我是电商珠珠 在抖店中&#xff0c;我们会看到一些店铺从来都不直播&#xff0c;官方账号的作品很少&#xff0c;但是他的店铺GMV依旧过万。 其实这就是抖音的货架电商所带来的优势&#xff0c;抖音现在主要由两部分组成&#xff0c;一部分是内容场&#xff0c;就是咱们平时刷…

SpringBoot整合MongoDB详解

SpringBoot——整合MongoDB详解 注意&#xff1a; MongoDB默认是本机访问 需要开启远程访问 window修改如下 linux 修改如下 /usr/local/mongodb4/mongodb.conf 详细内容 dbpath/data/mongodb4/mongo #数据文件保存地址 logpath/data/mongodb4/log/mongod.log #日志保存地址…

架构设计系列之基础:初探软件架构设计

11 月开始突发奇想&#xff0c;想把自己在公司内部做的技术培训、平时的技术总结等等的内容分享出来&#xff0c;于是就开通了一个 Wechat 订阅号&#xff08;灸哥漫谈&#xff09;&#xff0c;开始同步发送内容。 今天&#xff08;12 月 10 日&#xff09;也同步在 CSDN 上开通…

mysql字段设计规范:使用unsigned(无符号的)存储非负值

如果一个字段存储的是数值&#xff0c;并且是非负数&#xff0c;要设置为unsigned&#xff08;无符号的&#xff09;。 例如&#xff1a; 备注&#xff1a;对于类型是 FLOAT、 DOUBLE和 DECIMAL的&#xff0c;UNSIGNED属性已经废弃了&#xff0c;可能在mysql的未来某个版本去…

AI隆重软件,AI原创文章隆重软件

随着信息量的急剧增加&#xff0c;许多写作者、网站管理员和内容创作者们纷纷感受到了文章降重的压力。原始文本的降重&#xff0c;需要保留关键信息的同时避免重复&#xff0c;这是一项既繁琐又耗时的任务。 改写软件的批量降重功能 147SEO改写软件在降重领域的卓越表现主要体…

常见SSL证书都有哪些格式

常见Web服务软件 常见的Web服务软件&#xff0c;通常都基于OpenSSL和Java两种基础密码库。 Tomcat、Weblogic、JBoss等Web服务软件&#xff0c;一般使用Java提供的密码库。通过Java Development Kit&#xff08;JDK&#xff09;工具包中的Keytool工具&#xff0c;生成Java Ke…

android studio 按键点击事件的实现方法

一、onClick属性&#xff1a; 1&#xff09;、在activity_main.xml中设置button的onClick属性&#xff1a; <Buttonandroid:id"id/button"android:layout_width"wrap_content"android:layout_height"wrap_content"android:text"开灯&q…

SpringCloud系列(三)| 集成Nacos注册中心

好了万事具备&#xff0c;接下来我们就开始搭建我们SpringCloud项目。本章节我们先来搭建我们SpringCloud项目框架&#xff0c;然后开发两个服务&#xff0c;把他们注册到Nacos注册中心中。上一章节我们已经介绍了Nacos是可以作为注册中心和配置中心的&#xff0c;这一章节我们…

Linux centos8安装JDK1.8、tomcat

一、安装jdk 1.如果之前安装过jdk&#xff0c;先卸载掉旧的 rpm -qa | grep -i jdk 2.检查yum中有没有java1.8的包 yum list java-1.8* 3.yum安装jdk yum install java-1.8.0-openjdk* -y 4.验证 二、安装tomcat Index of /tomcat 可以在这里选择你想要安装的tomcat版本…

【unity实战】一个通用的FPS枪支不同武器射击控制脚本

文章目录 前言模型素材文章用到的粒子火光特效射击效果换弹瞄准开枪抖动效果设置显示文本最终代码不同武器射击效果1. 手枪2. 机枪3. 狙击枪4. 霰弹枪5. 加特林 其他感谢完结 前言 实现FPS枪支不同武器效果&#xff0c;比如手枪&#xff0c;喷子&#xff0c;狙击枪&#xff0c…