C++ 模拟实现string类

目录

一、类的定义

二、初始化&销毁

1、构造函数

2、辨析三种定义 

3、析构函数

三、赋值 

1、拷贝构造函数

2、赋值运算符

四、成员访问

 operator[ ]

五、比较大小&判断相等

六、容量操作 

1、size()

2、reserve

3、push_back

4、append

5、加等运算符 

6、C风格

7、insert

插入字符 

插入字符串

insert实现push_back&append

8、resize 

9、erase

10、swap

11、find 

七、迭代器

八、流输出流输入


在C++中,string类是一个非常重要的数据类型,它提供了一系列的方法来处理字符串。然而,你有没有想过string类是如何实现的呢?在这篇文章中,我们将模拟实现一个简化版的string类,这可以更好地理解其内部工作原理。

一、类的定义

我们首先定义了一个名为bit::string的类,它包含了一些私有成员变量和公有成员函数。

namespace bit
{class string{private:char* _str;size_t _capacity;size_t _size;static const size_t npos;//static const size_t npos = -1;//也可以public:// ...};const size_t string::npos = -1;
}

在这个类中,我们有三个私有成员变量:_str_capacity_size_str是一个字符指针,用于存储字符串的内容;_capacity表示字符串的容量,即可以存储的字符数量;_size表示当前字符串的长度。此外,我们还定义了一个静态常量npos,它的值为-1,通常用于表示“不存在”的位置。

二、初始化&销毁

1、构造函数

string(const char* str = ""):_size(strlen(str))
{_capacity = _size == 0 ? 3 : _size;_str = new char[_capacity + 1];strcpy(_str, str);
}
  1. 构造函数string(const char* str = "")使用了全缺省的方式,使用全缺省的构造函数,可以有以下几种调用方式:

    1. 不传递任何参数:string s;,这将创建一个空字符串的string对象。

    2. 传递一个空字符串:string s("");,这将创建一个空字符串的string对象。

    3. 传递一个非空字符串:string s("Hello");,这将创建一个包含指定字符串的string对象。

    4. 传递一个空指针:string s(nullptr);,这将创建一个空字符串的string对象。

  2. _size(strlen(str)): 这是一个初始化列表,它将_size成员变量初始化为传入字符串str的长度。这是通过使用C标准库函数strlen来实现的。

  3. _capaicty = _size == 0 ? 3 : _size;: 这行代码设置了_capacity成员变量的值。如果_size为0(也就是说,如果传入的字符串为空),那么_capacity被设置为3。否则,_capacity被设置为_size的值。

  4. _str = new char[_capaicty + 1];: 这行代码在堆上为_str分配了足够的内存来存储字符串。分配的内存大小为_capacity + 1,额外的1用于存储字符串的空字符终止符\0

  5. strcpy(_str, str);: 这行代码将传入的字符串str复制到_str指向的内存中。这是通过使用C标准库函数strcpy来实现的。

2、辨析三种定义 

		string(const char* str = nullptr)  不可以string(const char* str = '\0')	 不可以string(const char* str = "\0")	 可以
  • string(const char* str = nullptr): 这个构造函数版本不可行,因为nullptr不能被strlenstrcpy处理,这会导致未定义的行为。

  • string(const char* str = '\0'): 这个构造函数版本也不可行,因为'\0'是一个字符,而不是一个字符串。它不能被赋值给const char*类型的参数。

  • string(const char* str = "\0"): 这个构造函数版本是可行的,因为"\0"是一个包含空字符的字符串。然而,它与string(const char* str = "")的行为是相同的,因为在C++中,字符串总是以空字符终止。

3、析构函数

析构函数则负责释放_str指向的内存,并将所有成员变量重置为初始状态。 

~string()
{delete[] _str;_str = nullptr;_size = _capaicty = 0;
}

 

三、赋值 

1、拷贝构造函数

拷贝构造函数的目的是创建一个与参数对象s具有相同字符串和相同容量的新string对象。

string(const string& s):_size(s._size), _capacity(s._capacity)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);
}

  1. string(const string& s): 这是复制构造函数的声明。它接受一个对已存在的字符串对象的引用作为参数。使用引用是为了避免无限递归,因为如果不使用引用,那么在创建新的字符串对象时会再次调用复制构造函数,如此无限循环下去。

  2. _size(s._size): 这是初始化列表的一部分,它将新字符串对象的 _size 成员变量设置为传入的字符串对象的 _size

  3. _capaicty(s._capaicty): 同样是初始化列表的一部分,它将新字符串对象的 _capaicty 成员变量设置为传入的字符串对象的 _capaicty

  4. _str = new char[s._capaicty + 1];: 这行代码为新字符串对象的 _str 成员变量分配内存。它创建一个新的字符数组,大小为传入的字符串对象的 _capaicty 加 1。这里加 1 是为了留出空间存储字符串的结束字符 '\0'。

  5. strcpy(_str, s._str);: 这行代码将传入的字符串对象的 _str 成员变量的内容复制到新字符串对象的 _str 成员变量中。

2、赋值运算符

这段代码是C++中的赋值运算符重载函数,用于将一个字符串对象赋值给另一个字符串对象。这也是一个深拷贝操作,因为它创建了新的内存空间来存储复制的字符串,而不是简单地复制指针。

string& operator=(const string& s)
{if (this != &s){char* tmp = new char[s._capaicity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capaicity = s._capacity;}return *this;
}
  1. string& operator=(const string& s): 这是赋值运算符重载函数的声明。它接受一个对已存在的字符串对象的引用作为参数,并返回对当前对象的引用,以便支持链式赋值(例如 a = b = c)。

  2. if (this != &s): 这是一个保护性检查,用于防止自我赋值。如果尝试将一个对象赋值给它自己,那么这个检查将阻止后续的代码执行。

  3. char* tmp = new char[s._capaicity + 1];: 这行代码创建一个新的字符数组,大小为传入的字符串对象的 _capaicity 加 1。这里加 1 是为了留出空间存储字符串的结束字符 '\0'。

  4. strcpy(tmp, s._str);: 这行代码将传入的字符串对象的 _str 成员变量的内容复制到新创建的字符数组中。

  5. delete[] _str;: 这行代码释放当前对象的 _str 成员变量所占用的内存。这是为了避免内存泄漏,因为 _str 将被重新赋值。

  6. _str = tmp;: 这行代码将 _str 成员变量设置为新创建的字符数组的地址。

  7. _size = s._size;: 这行代码将当前对象的 _size 成员变量设置为传入的字符串对象的 _size

  8. _capaicity = s._capaicity;: 这行代码将当前对象的 _capaicity 成员变量设置为传入的字符串对象的 _capaicity

  9. return *this;: 这行代码返回对当前对象的引用,以支持连续赋值。

四、成员访问

我们为这个类重载了一些运算符,以便可以像使用内置类型一样使用我们自己定义的string类。

 operator[ ]

const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
  1. const char& operator[](size_t pos) const: 这是一个重载的下标运算符,它接受一个位置参数,并返回该位置的字符的引用。这个版本的函数是常量版本,它不能用于修改字符串的内容,只能用于读取。如果位置超出字符串的大小,它会触发一个断言错误。

  2. char& operator[](size_t pos): 这也是一个重载的下标运算符,它接受一个位置参数,并返回该位置的字符的引用。这个版本的函数可以用于修改字符串的内容。如果位置超出字符串的大小,它也会触发一个断言错误。

使用[ ]时会自动选取合适的函数重载,这两种重载版本的存在,使得我们可以在保证类型安全的同时,对常量和非常量字符串对象进行适当的操作。

五、比较大小&判断相等

这些函数都是常量的,这意味着它们不能修改字符串对象的状态。这些函数都接受一个对 string 对象的常量引用作为参数,这可以避免不必要的复制操作。

bool operator>(const string& s) const
{return strcmp(_str, s._str) > 0;
}bool operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}bool operator>=(const string& s) const
{//return *this > s || *this == s;return *this > s || s == *this;
}bool operator<(const string& s) const
{return !(*this >= s);
}bool operator<=(const string& s) const
{return !(*this > s);
}bool operator!=(const string& s) const
{return !(*this == s);
}
  1. bool operator>(const string& s) const: 这个函数重载了大于运算符 >。它使用 strcmp 函数比较两个字符串,如果当前字符串大于参数字符串,strcmp 会返回一个大于0的值,所以函数返回 true

  2. bool operator==(const string& s) const: 这个函数重载了等于运算符 ==。它使用 strcmp 函数比较两个字符串,如果两个字符串相等,strcmp 会返回0,所以函数返回 true

  3. bool operator>=(const string& s) const: 这个函数重载了大于等于运算符 >=。它使用已经重载的 > 和 == 运算符来判断当前字符串是否大于或等于参数字符串。注释掉的代码有误,应该是 return *this > s || *this == s;

  4. bool operator<(const string& s) const: 这个函数重载了小于运算符 <。它使用已经重载的 >= 运算符来判断当前字符串是否不大于等于参数字符串,即小于参数字符串。

  5. bool operator<=(const string& s) const: 这个函数重载了小于等于运算符 <=。它使用已经重载的 > 运算符来判断当前字符串是否不大于参数字符串,即小于或等于参数字符串。

  6. bool operator!=(const string& s) const: 这个函数重载了不等于运算符 !=。它使用已经重载的 == 运算符来判断当前字符串是否不等于参数字符串。

六、容量操作 

1、size()

size_t size() const
{return _size;
}

size_t size() const: 这个函数返回字符串的大小,即字符串中的字符数量。这个函数是常量函数,这意味着它不能修改字符串的状态。

2、reserve

void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
  1. if (n > _capacity): 检查新的容量 n 是否大于当前的容量 _capacity。如果不是,那么函数立即返回,不做任何操作。

  2. char* tmp = new char[n + 1];: 如果新的容量大于当前的容量,那么首先创建一个新的字符数组,大小为新的容量加 1。这里加 1 是为了留出空间存储字符串的结束字符 '\0'。

  3. strcpy(tmp, _str);: 然后将当前字符串的内容复制到新的字符数组中。

  4. delete[] _str;: 然后释放当前字符串的内存。这是为了避免内存泄漏,因为 _str 将被重新赋值。

  5. _str = tmp;: 然后将 _str 指向新的字符数组。这样,_str 就指向了一个更大的内存区域,可以存储更多的字符。

  6. _capacity = n;: 最后更新字符串的容量为新的容量。

3、push_back

void push_back(char ch)
{if (_size + 1 > _capacity){reserve(_capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0';
}

void push_back(char ch): 这个函数在字符串的末尾添加一个字符。如果添加新字符后字符串的大小超过了当前的容量,那么它会首先调用 reserve 函数来增加字符串的容量。然后它将新字符添加到字符串的末尾,并更新字符串的大小,最后不要忘记补充‘ \0 ’在新的字符串末尾。 

4、append

void append(const char* str)
{size_t len = strlen(str);if (_size+len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;
}

void append(const char* str): 这个函数在字符串的末尾添加一个C风格字符串。它首先计算要添加的字符串的长度,如果添加新字符串后字符串的大小超过了当前的容量,那么它会首先调用 reserve 函数来增加字符串的容量。然后它将新字符串添加到当前字符串的末尾,并更新字符串的大小。

5、加等运算符 

这两个函数提供了一种简洁的方式来修改字符串的内容,它们可以用来代 push_back 和 append 函数。

string& operator+=(char ch)
{push_back(ch);return *this;
}string& operator+=(const char* str)
{append(str);return *this;
}
  1. string& operator+=(char ch): 这个函数接受一个字符作为参数,并在字符串的末尾添加这个字符。它调用了 push_back 函数来完成这个操作。然后它返回对当前对象的引用,以支持链式操作,例如 str += 'a' += 'b'

  2. string& operator+=(const char* str): 这个函数接受一个C风格字符串作为参数,并在字符串的末尾添加这个字符串。它调用了 append 函数来完成这个操作。然后它也返回对当前对象的引用,以支持链式操作,例如 str += "hello" += " world"

6、C风格

const char* c_str()
{return _str;
}

const char* c_str(): 这个函数返回一个指向字符串内部字符数组的指针。这个函数通常用于与需要C风格字符串的函数进行交互。返回类型为 const char*,意味着你不能通过这个指针修改字符串的内容。 

7、insert

这两个 insert 函数用于在 string 对象的指定位置插入字符或字符串。

插入字符 

 下面的代码有什么问题呢?

void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size + 1 > _capacity){reserve(2 * _capacity);}size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;
}
  • 这段代码的主要问题在于 while 循环中的条件判断。在 C++ 中,size_t 是无符号整数类型,当 end 为 0 时,--end 会导致 end 变为最大的无符号整数,而不是 -1。因此,当 pos 为 0 时,end >= pos 的判断条件始终为 true,这会导致无限循环
  • 在这个无限循环中,程序会尝试访问 _str[end],其中 end 是一个非常大的数。这将导致数组越界,可能会引发运行时错误,或者导致未定义的行为。

对上述代码进行改进: 

string& insert(size_t pos, char ch)
{assert(pos <= _size);if (_size + 1 > _capacity){reserve(2 * _capacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end-1];--end;}_str[pos] = ch;++_size;return *this;
}
  1. string& insert(size_t pos, char ch): 这个函数在指定位置 pos 插入一个字符 ch

  2. 首先,它检查位置是否有效,然后检查是否需要增加字符串的容量。

  3. 接下来,它将从位置 pos 开始的所有字符向后移动一位,为新字符腾出空间。

  4. 然后,它在位置 pos 插入新字符,并更新字符串的大小。

  5. 最后,它返回对当前对象的引用,以支持链式操作。

插入字符串

string& insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}strncpy(_str + pos, str, len);_size += len;return *this;}
  1. string& insert(size_t pos, const char* str): 这个函数在指定位置 pos 插入一个C风格字符串 str

  2. 首先,它检查位置是否有效,然后计算要插入的字符串的长度,然后检查是否需要增加字符串的容量。

  3. 然后,它将从位置 pos 开始的所有字符向后移动 len 位,为新字符串腾出空间。然后,它在位置 pos 插入新字符串,并更新字符串的大小。

  4. 最后,它也返回对当前对象的引用,以支持链式操作。

insert实现push_back&append

void push_back(char ch)
{//if (_size + 1 > _capacity)//{//	reserve(_capacity * 2);//}//_str[_size] = ch;//++_size;//_str[_size] = '\0';insert(_size, ch);
}void append(const char* str)
{//size_t len = strlen(str);//if (_size+len > _capacity)//{//	reserve(_size + len);//}//strcpy(_str + _size, str);strcat(_str, str);//_size += len;insert(_size, str);
}

8、resize 

这个函数提供了一种灵活的方式来改变字符串的大小,无论是增大还是减小。如果需要增大字符串的大小,那么可以选择一个填充字符。如果需要减小字符串的大小,那么将删除多余的字符。

void resize(size_t n, char ch = '\0')
{if (n < _size){// 删除数据--保留前n个_size = n;_str[_size] = '\0';}else if (n > _size){if (n > _capacity){reserve(n);}size_t i = _size;while (i < n){_str[i] = ch;++i;}_size = n;_str[_size] = '\0';}
}

这个 resize 函数用于改变 string 对象的大小。它接受两个参数:新的大小 n 和用于填充的字符 ch,默认为 '\0'。

  1. if (n < _size): 如果新的大小 n 小于当前的大小 _size,那么将删除多余的字符。这通过将 _size 设置为 n,并在新的末尾位置添加结束字符 '\0' 来实现。

  2. else if (n > _size): 如果新的大小 n 大于当前的大小 _size,那么将添加额外的字符。

  3. if (n > _capacity): 如果新的大小 n 大于当前的容量 _capacity,那么将调用 reserve 函数来增加字符串的容量。
  4. while (i < n): 然后使用一个循环来添加额外的字符。每个额外的字符都被设置为 ch
  5. _size = n;: 最后更新字符串的大小为新的大小 n,并在新的末尾位置添加结束字符 '\0'。

9、erase

string& erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}
  1. 函数定义string& erase(size_t pos, size_t len = npos) 定义了一个名为 erase 的成员函数,它属于某个 string 类型。它接受两个参数:pos 和 lenpos 是要开始删除的位置索引(从零开始),而 len 是要删除的字符数。如果 len 没有指定,默认值是 npos,这通常表示字符串的最大长度,意味着从 pos 开始删除到字符串末尾。

  2. assert(pos < _size); 这一行是一个调试时使用的断言,确保 pos 小于当前字符串的大小 _size。如果 pos 大于等于 _size,程序会在调试模式下终止。

  3. 检查 len 和删除操作:

    • 如果 len 等于 npos 或 pos + len 大于等于 _size(意味着要删除的范围超出了字符串的末尾),那么只需将位置 pos 处的字符设置为 \0(空字符),表示字符串在这里结束。然后更新 _size 为 pos,相当于删除了从 pos 到字符串末尾的所有字符。
    • 如果不是上述情况,则执行 else 里的代码。这里使用 strcpy(_str + pos, _str + pos + len); 来删除指定的字符。这个操作实际上是将 pos + len 之后的字符串复制到 pos 处,从而覆盖掉要删除的部分。然后更新 _size 来反映新的字符串长度,即减去删除的字符数。
  4. 返回值: 函数返回 *this,即返回经过修改后的字符串对象的引用。这允许链式调用,例如 str.erase(1, 3).append("abc");

10、swap

通过使用std::swap函数,它交换了两个对象的成员变量,包括字符串内容、容量和大小。

void swap(string& s)
{std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);
}

11、find 

size_t find(char ch, size_t pos = 0)
{assert(pos < _size);for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;}}return npos;
}
size_t find(const char* str, size_t pos = 0)
{assert(pos < _size);// kmpchar* p = strstr(_str + pos, str);if (p == nullptr){return npos;}else{return p - _str;}
}

这两个函数都是 string 类的 find 方法的实现,用于在字符串中查找特定的字符或子字符串。我将逐步解释它们的作用和逻辑:

  1. 函数定义size_t find(char ch, size_t pos = 0) 和 size_t find(const char* str, size_t pos = 0) 定义了两个重载的 find 函数。第一个函数用于查找单个字符 ch,第二个函数用于查找子字符串 str。两个函数都接受一个可选的起始位置 pos,默认值为 0,表示从字符串的开始位置进行查找。

  2. 断言assert(pos < _size); 这一行是一个调试时使用的断言,确保 pos 小于当前字符串的大小 _size。如果 pos 大于等于 _size,程序会在调试模式下终止。

  3. 查找字符:在第一个 find 函数中,从位置 pos 开始,遍历字符串中的每个字符,如果找到与 ch 相等的字符,就返回其位置。如果遍历完整个字符串都没有找到,就返回 npos,通常表示字符串的最大长度,这里用作查找失败的标志。

  4. 查找子字符串:在第二个 find 函数中,使用 strstr 函数从位置 pos 开始查找子字符串 strstrstr 是 C/C++ 标准库中的一个函数,用于在一个字符串中查找另一个字符串的首次出现位置。如果找到,strstr 返回第一次出现的位置的指针,否则返回 nullptr。如果 strstr 返回 nullptr,表示没有找到子字符串,函数返回 npos。否则,返回找到的位置,即 p - _str

七、迭代器

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;
}
  1. typedef char* iterator;: 这行代码定义了一个类型别名 iterator,它是 char* 类型的别名。这意味着你可以使用 iterator 来代替 char*

  2. typedef const char* const_iterator;: 这行代码定义了一个类型别名 const_iterator,它是 const char* 类型的别名。这意味着你可以使用 const_iterator 来代替 const char*

  3. iterator begin(): 这个函数返回一个指向字符串开始的迭代器。在这个 string 类中,字符串的开始就是 _str 成员变量的地址。

  4. iterator end(): 这个函数返回一个指向字符串结束的迭代器。在这个 string 类中,字符串的结束就是 _str 成员变量的地址加上字符串的大小 _size

  5. const_iterator begin() const: 这是 begin() 函数的常量版本,它返回一个指向常量字符串开始的常量迭代器。这个函数是常量的,这意味着它不能修改字符串对象的状态。

  6. const_iterator end() const: 这是 end() 函数的常量版本,它返回一个指向常量字符串结束的常量迭代器。这个函数也是常量的。

八、流输出流输入

ostream& operator<<(ostream& out, const string& s):这是输出流操作符<<的重载函数,用于将string对象s输出到输出流out

ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}
  1. for (auto ch : s):这是一个范围for循环,用于遍历字符串s中的每一个字符ch
  2. out << ch:这行代码将字符ch输出到输出流out
  3. return out:最后,函数返回输出流out,以便进行链式操作。

在类中定义clear()成员函数用于清空 string 对象中的字符串。

void clear()
{_str[0] = '\0';size = 0;
}

 istream& operator>>(istream& in, string& s):这是输入流操作符>>的重载函数,用于从输入流in读取数据到string对象s。 

istream& operator>>(istream& in, string& s)
{s.clear();char ch = in.get();char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}
  1. 首先,函数接收两个参数,一个是输入流对象in,另一个是字符串s。这个函数的目的是从输入流in中读取字符,然后将这些字符存储到字符串s中。

  2. 在函数开始时,首先清空字符串s,以确保它是空的。

  3. 然后,使用in.get()函数从输入流中获取一个字符,并将其存储在变量ch中。

  4. 定义一个字符数组buff,用于临时存储从输入流中读取的字符。同时定义一个变量i,用于跟踪buff中的当前位置。

  5. 接下来是一个循环,该循环会一直执行,直到从输入流中读取的字符是空格或换行符。在循环中,首先将从输入流中读取的字符存储到buff中,然后增加i的值。

  6. 如果i的值达到127(即buff的最大容量),则将buff的最后一个字符设置为\0(表示字符串的结束),然后将buff中的内容添加到字符串s中,并将i重置为0。

  7. 在每次循环的结束,都会从输入流中获取一个新的字符。

  8. 循环结束后,如果buff中还有未添加到s的字符(即i不等于0),则将buff的最后一个字符设置为\0,然后将buff中的内容添加到s中。

  9. 最后,函数返回输入流对象in,以便可以链式地进行更多的输入操作。

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

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

相关文章

onnx检测推理

起因&#xff1a;当我想把检测的onnx模型转换到特定的设备可以使用的模型时&#xff0c;报错do not support dimension size > 4&#xff0c;onnx中有些数据的维度是五维&#xff0c;如图。本文使用的是edgeyolo&#xff0c;它使用的是yolox的head&#xff0c;最后的输出加上…

gmid方法设计五管OTA二级远放

首先给出第一级是OTA&#xff0c;第二级是CS的二级运放电路图&#xff1a; gmid的设计方法可以根据GBW、Av、CL来进行电路设计&#xff0c;因此在设计电路之前需要以上的参数要求。 1、为了满足电路的相位裕度至少60&#xff0c;需要对GBW、主极点、零点进行分析。 首先给出其…

应用程序无法找到xinput1_3.dll怎么办,xinput1_3.dll 丢失的解决方法

当电脑系统或特定应用程序无法找到或访问到 xinput1_3.dll 文件时&#xff0c;便会导致错误消息的出现&#xff0c;例如“找不到 xinput1_3.dll”、“xinput1_3.dll 丢失”等。这篇文章将大家讨论关于 xinput1_3.dll 文件的内容、xinput1_3.dll丢失问题的解决方法&#xff0c;以…

查收查引(通过文献检索开具论文收录或引用的检索证明)

开具论文收录证明的 专业术语为 查收查引&#xff0c;是高校图书馆、情报机构或信息服务机构提供的一项有偿服务。 因检索需要一定的时间&#xff0c;提交委托时请预留足够的检索时间。 一般需要提供&#xff1a;论文题目、作者、期刊名称、发表年代、卷期、页码。 目录 一、查…

MIT6.5840-2023-Lab1: MapReduce

前置知识 MapReduce&#xff1a;Master 将一个 Map 任务或 Reduce 任务分配给一个空闲的 worker。 Map阶段&#xff1a;被分配了 map 任务的 worker 程序读取相关的输入数据片段&#xff0c;生成并输出中间 k/v 对&#xff0c;并缓存在内存中。 Reduce阶段&#xff1a;所有 ma…

QT 中基于 TCP 的网络通信 (备查)

基础 基于 TCP 的套接字通信需要用到两个类&#xff1a; 1&#xff09;QTcpServer&#xff1a;服务器类&#xff0c;用于监听客户端连接以及和客户端建立连接。 2&#xff09;QTcpSocket&#xff1a;通信的套接字类&#xff0c;客户端、服务器端都需要使用。 这两个套接字通信类…

位运算在查找重复元素中的妙用

关卡名 用4KB内存寻找重复元素 我会了✔️ 内容 1.理解如何用4KB内存寻找重复元素 ✔️ 本关所有题目的重点都是理解如何解决就好&#xff0c;面试问的时候能够将问题描述清楚&#xff0c;不用写代码。 在海量数据中&#xff0c;此时普通的数组、链表、Hash、树等等结构有…

《当代家庭教育》期刊论文投稿发表简介

《当代家庭教育》杂志是家庭的参谋和助手&#xff0c;社会的桥梁和纽带&#xff0c;人生的伴侣和知音&#xff0c;事业的良师益友。 国家新闻出版总署批准的正规省级教育类G4期刊&#xff0c;知网、维普期刊网收录。安排基础教育相关稿件&#xff0c;适用于评职称时的论文发表…

mybatisplus自动填充属性值

MetaObjectHandler: 是mybatisplus提供的一个接口&#xff0c;&#xff0c;&#xff0c;这个接口定义了在执行插入和更新操作的时候的回调方法&#xff0c;&#xff0c;&#xff0c;允许你自定义实体对象的一些属性值&#xff0c;&#xff0c;比如: createTime,createBy,update…

java中static关键字的作用是什么?

在Java中&#xff0c;static 是一个关键字&#xff0c;它可以用来修饰类的成员变量、成员方法和内部类。static 的作用主要有以下几个方面&#xff1a; 静态变量&#xff08;Static Variables&#xff09;&#xff1a; 当变量被声明为static时&#xff0c;它变成了类的静态变量…

今天面试招了个25K的测试员,从腾讯出来的果然都有两把刷子···

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;前面一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在15-25k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是4年工作经验&#xff0c;但面试中&#xff0c;不…

OkGo导入失败解决办法

jcenter()maven { url "https://jitpack.io" }再同步就可以了

罗马数字转整数算法(leetcode第13题)

题目描述&#xff1a; 罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M …

微服务主要特点Java微服务开发的关键技术

【点我-这里送书】 本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(…

c++ atmoic acquire/release

由于多核cpu缓存的存在&#xff0c;以及gcc编译优化&#xff0c;cpu指令层面的优化&#xff0c;导致程序的执行顺序可能跟你写的顺序不完全一致&#xff08;reorder&#xff09;。 但是在多线程编程中如何确保各个线程能正确的读取到各个变量呢&#xff08;而不是cache中老旧的…

【Vue3从入门到项目实现】RuoYi-Vue3若依框架前端学习——登录页面

若依官方的前后端分离版中&#xff0c;前端用的Vue2&#xff0c;这个有人改了Vue3的前端出来。刚好用来学习&#xff1a; https://gitee.com/weifengze/RuoYi-Vue3 运行前后端项目 首先运行项目 启动前端&#xff0c;npm install、npm run dev 启动后端&#xff0c;按教程配置…

Spring第四课,MVC终章,应用分层的好处,总结

Spring MVC其实也就是Spring Web 软件的设计原则&#xff1a;高内聚&#xff0c;低耦合 高内聚:一个模块各个元素之间联系的紧密程度&#xff0c;如果各个元素&#xff08;语句&#xff0c;程序段&#xff09;之间的联系程度越高&#xff0c;即内聚性越高 低耦合&#xff1a;软…

自定义登录页面模板(移动端)

login/index <script setup lang"ts"> </script><template><div class"login-page">//组件 由于配置了自动注册&#xff0c;所以无需引入<cp-nav-barright-text"注册"click-right"$router.push(/register)&quo…

Codeforces Round 913 (Div. 3)补题

Rook 题目大意&#xff1a;我们给定一个棋盘(如下图)&#xff0c;棋盘上有一个车&#xff0c;问车可以到的位置&#xff0c;任意顺序输出即可。 思路&#xff1a;输出车的行列中非它本身的点即可。 #include<bits/stdc.h> using namespace std; int main() {int t;scanf…