赋值运算符重载以及析构函数
发布时间:2025-06-24 21:07:36 作者:北方职教升学中心 阅读量:489
现代写法以默认构造用String s中数据构造了一个临时对象,然后交换了目标对象与临时对象内部的_str,将tmp管理的空间交给了目标对象。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的
容量最后:还有一个指针做一些其他事情。
总结:写时拷贝技术可以视作一种博弈,如果程序员需要就该就分配空间,不需要就不分配空间。
2.2g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个
指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小
字符串有效长度
引用计数指向堆空间的指针,用来存储字符串。
#include"string.h"namespace zlr{ const size_t string::npos = -1; void string::reserve(size_t n) { if (n > _capacity)//判断空间是否足够 { //cout << "reserve:" << n << endl; char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } void string::push_back(char ch) { if (_size == _capacity)//判断空间 { reserve(_capacity == 0 ? 4 : _capacity * 2); } _str[_size] = ch;//插入新数据 ++_size; _str[_size] = '\0';//原来的'\0'被覆盖,加上新'\0' } string& string::operator+=(char ch) { push_back(ch);//复用尾插的代码 return *this; } void string::append(const char* str) { size_t len = strlen(str); if (_size + len > _capacity) { // 大于2倍,需要多少开多少,小于2倍按2倍扩 reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity); } strcpy(_str + _size, str); _size += len; } string& string::operator+=(const char* str) { append(str); return *this; } void string::insert(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } // 挪动数据 size_t end = _size + 1; while (end > pos) { _str[end] = _str[end - 1]; --end; } _str[pos] = ch; ++_size; } void string::insert(size_t pos, const char* s) { assert(pos <= _size); size_t len = strlen(s); if (_size + len > _capacity) { // 大于2倍,需要多少开多少,小于2倍按2倍扩 reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity); } size_t end = _size + len; while (end > pos + len - 1) { _str[end] = _str[end - len]; --end; } for (size_t i = 0; i < len; i++) { _str[pos + i] = s[i]; } _size += len; } void string::erase(size_t pos, size_t len) { assert(pos < _size); if (len >= _size - pos)//如果要删除的长度大于剩下的长度 //就全部删除 { _str[pos] = '\0'; _size = pos; } else { for (size_t i = pos + len; i <= _size; i++) { _str[i - len] = _str[i]; } _size -= len; } } size_t string::find(char ch, size_t pos) { assert(pos < _size); for (size_t i = pos; i < _size; i++) { if (_str[i] == ch) { return i; } } return npos; } size_t string::find(const char* str, size_t pos) { assert(pos < _size); const char* ptr = strstr(_str + pos, str); if (ptr == nullptr) { return npos; } else { return ptr - _str; } } string string::substr(size_t pos, size_t len) { assert(pos < _size); // len大于剩余字符长度,更新一下len if (len > _size - pos) { len = _size - pos; } string sub; sub.reserve(len); for (size_t i = 0; i < len; i++) { sub += _str[pos + i]; } return sub; } bool operator<(const string& s1, const string& s2)//复用库中的比较 { return strcmp(s1.c_str(), s2.c_str()) < 0; } bool operator<=(const string& s1, const string& s2) { return s1 < s2 || s1 == s2;//复用已完成的逻辑 } bool operator>(const string& s1, const string& s2) { return !(s1 <= s2); } bool operator>=(const string& s1, const string& s2) { return !(s1 < s2); } bool operator==(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) == 0; } bool operator!=(const string& s1, const string& s2) { return !(s1 == s2); } ostream& operator<<(ostream& out, const string& s) { for (auto ch : s) { out << ch; } return out; } istream& operator>>(istream& in, string& s) { s.clear();//先清理数据 const int N = 256;//模拟库中的string的buff char buff[N]; int i = 0; char ch; //in >> ch; ch = in.get();//cin提取默认空格作为间隔符,无法提取间隔符 while (ch != ' ' && ch != '\n') { buff[i++] = ch; if (i == N-1) { buff[i] = '\0'; s += buff; i = 0; } //in >> ch; ch = in.get(); } if (i > 0) { buff[i] = '\0'; s += buff; } return in; }}
2、在面试中,面试官总喜欢让
应试者自己来模拟实现string类,最主要是实现string类的构造、所谓深拷贝就是先为目标对象申请单独的空间,然后将要拷贝的数据拷贝到新开辟的空间中
1.4 String类拷贝构造与operator=的传统与现代写法
1.4.1传统写法
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;};
传统写法就是按照我们的直观感觉写,拷贝构造我们开辟对应大小的空间然后将数据拷贝过去就行了;赋值重载我们同样开辟对应空间。赋值运算符重载以及析
构函数。
说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会自动生成默认
的,当用s1构造s2时,编译器会调用默认的拷贝构造。s2共用同一块内
存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。复用了已完成的逻辑。如果程序员不需要通过某一个对象修改数据,那编译器就不分配空间资源。 vs和g++下string结构的说明
2.1vs下string的结构
2.2g++下string的结构
3. 写时拷贝(了解)
4. 扩展阅读
1、最终导致的问题是,s1、#define _CRT_SECURE_NO_WARNINGS 1#pragma once#include<iostream>#include<string>#include<assert.h>using namespace std;namespace zlr{ class string { public: 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; } /*string() :_str(new char[1]{'
#define _CRT_SECURE_NO_WARNINGS 1#pragma once#include<iostream>#include<string>#include<assert.h>using namespace std;namespace zlr{ class string { public: 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; } /*string() :_str(new char[1]{'