• 实现STL(SGI)的string(深拷贝)


    解决浅拷贝问题

    如题:
    在这里插入图片描述

    什么是浅拷贝

    浅拷贝:也称位拷贝,编译器只是将对象中的拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时其余对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了错误。要解决浅拷贝问题,通过深拷贝来解决。

    浅拷贝 深拷贝在这里插入图片描述

    在这里插入图片描述
    而在进行析构释放堆上new出来的资源时候就会出现重复释放的错误,如下图所示,当s1 destory,_s申请的空间被释放,但是!!因为s1和s2中是同一个资源,当s2释放时改空间已经是一块被释放的空间,再进行释放就会造成非法访问。
    在这里插入图片描述
    浅拷贝问题:

    1. 可能会造成内存泄漏
    2. 资源重复释放会使程序运行崩溃

    所以当类中涉及动态内存管理时,一定要预防浅拷贝发生,使用深拷贝

    深拷贝

    在这里插入图片描述

    实现代码:

    class String
    {
      public:
          String(const char *str = NULL); // 通用构造函数
          String(const String &another); // 拷贝构造函数
          ~ String(); // 析构函数
          String & operater =(const String &rhs); // 赋值函数
      private:
          char *m_data; // 用于保存字符串
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    有几种深拷贝的方法如下:

    1. 深拷贝 1.0
    // 通用构造函数
    String::String(const char *str = NULL){
    	m_data = new char[strlen(str) + 1];
    	strcpy(m_data, str);
    }
    // 深拷贝 1.0
    String::String(const String &another){
    	m_data = new char[strlen(another) + 1];  //+1加上字符串结尾的'/0'
    	strcpy(m_data, str);
    }
    // 析构函数
    String::~String(){
    	if (m_data != nullptr)
    		delete[] m_data;
    }
    // 赋值运算符重载函数
    String::String& operator=(const String& rhs){
    	if (this != &rhs){   //不是给自己赋值
    		// 申请新空间
    		char* temp = new char[strlen(rhs.m_data) + 1];
    		// 拷贝元素
    		strcpy(temp, rhs.m_data);
    		// 释放旧空间
    		delete[] m_data;
    			// 使用新空间
    		m_data = temp;
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1. 深拷贝 2.0
    // 深拷贝 2.0
    String::String(const String &another){
    	if(this != &another){
    		String temp(another.m_data);
    		swap(m_data, temp.m_data);
    	}
    }
    
    // 赋值运算符重载函数
    String::String& operator=(const String& rhs){
    	if(this != &another){
    		String temp(another.m_data);
    		swap(m_data, temp.m_data);
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.0 简化版

    // 深拷贝 2.1
    String::String(const String &another){
    	String temp(another.m_data);
    	swap(m_data, temp.m_data);
    }
    
    // 赋值运算符重载函数
    String::String& operator=(const String& rhs){
    	swap(m_data,rhs.m_data); // 传参时拷贝构造临时对象
    	return *this;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    或通过写时拷贝解决

    写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
    引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源

    (智能指针思想)

    实现string

    https://cplusplus.com/reference/string

    源代码

    #ifndef _STRING_H_
    #define _STRING_H_
    
    #include "stdhead.h"
    
    
    
    namespace my_stl{
    	class string{
    	public:
    		// 迭代器(原生指针)
    		typedef char* iterator;
    		typedef char* reverse_iterator;
    
    		/*  Member functions  */
    		// 构造函数
    		string(const char* str = ""){
    			if (str == NULL) assert(false);
    			_size = strlen(str);
    			_str = new char[_size + 1];  //+1加上字符串结尾的'/0'
    			strcpy(_str, str);
    			_capacity = _size;
    		}
    		string(size_t n, char ch){
    			_str = new char[n + 1];
    			_str[n] = '\0';
    			_size = _capacity = n;
    		}
    		// 拷贝构造
    		string(const string& s) :_str(NULL){
    			string temp(s._str);
    			this->swap(temp);
    		}
    		// 赋值运算符重载
    		string& operator=(string s){
    			swap(s); // 传参时拷贝构造临时对象
    			return *this;
    		}
    
    		~string(){
    			if (_str != NULL){
    				delete[] _str;
    				_str = NULL;
    				_size = 0;
    				_capacity = 0;
    			}
    		}
    
    		/* Iterators: */
    		iterator begin(){ return _str; }
            iterator end(){ return _str + _size; }
    
    		reverse_iterator rbegin(){	return end(); }
    		reverse_iterator rend(){ return begin(); } 
    
    		/* Capacity: */
    		size_t size()const{ return _size;	}
            size_t length()const{ return _size; }
        	size_t capacity()const{ return _capacity; }
    		bool empty()const{ return _size == 0; }
            void clear(){
    			_str[0] = '\0';
    			_size = 0;
    			return;
    		}
    
    		void resize(size_t newsize, char ch){
    			size_t oldsize = _size;
    			if (oldsize > newsize) _str[newsize] = '\0';
    			else{
    				if (newsize > _capacity){
    					// 扩容
    					reserve(2 * _capacity);  // 以2倍方式进行扩容
    					append(newsize - oldsize, ch);
    				}
    				_size = newsize;
    			}
    		}
    		void resize(size_t  newsize){
    			resize(newsize, char());
    		}
    
    		void reserve(size_t newcapacity){
    			size_t oldcapacity = _capacity;
    			if (oldcapacity < newcapacity){
    				char* temp = new char[newcapacity + 1];
    				strcpy(temp, _str);
    				delete[] _str;
    				_str = temp;
    				_capacity = newcapacity;
    			}
    		}
    
    		/* Element access: */
    		char& operator[](size_t index){
    			assert(index < _size);
    			return _str[index];
    		}
    		const char& operator[](size_t index)const{
    			assert(index < _size);
    			return _str[index];
    		}
    
    		char& back(){ return _str[_size - 1]; }
    		const char& back() const{ return _str[_size - 1]; }
    		char& front(){ return _str[0]; }
    		const char& front() const{ return _str[0]; }
    
    		/* Modifiers: */
    		string& operator+=(char ch){
    			push_back(ch);
    			return *this;
    		}
    		string& operator+=(const char* str){
    			append(str);
    			return *this;
    		}
    		string& operator+=(const string& s){
    			append(s.c_str());
    			return *this;
    		}
    
    		string& append(size_t n, char ch){
    			if (n + _size > _capacity)
    				reserve(n + _size);
    			memset(_str + _size, ch, n);
    			_size += n;
    			_str[_size] = '\0';
    			return *this;
    		}
    		string& append(const char* str){
    			size_t size = strlen(str);
    			if (size + _size > _capacity)
    				reserve(size + _size);
    			strcat(_str, str);
    			_size += size;
    			return *this;
    		}
    		void push_back(char ch){ append(1, ch); }
    		string& insert(size_t pos, const string& s){
    			if (_size + s._size > _capacity){
    				reserve(_size + s._size);
    			}
    			// 搬移元素
    			for (int i = _size; i >= (int)pos; --i){
    				_str[i+s._size] = _str[i];
    			}
    			strncpy(_str + pos, s._str, s._size);
    			_size += s._size;
    			return *this;
    		}
    
    		string& erase(size_t pos = 0, size_t n = npos){
    			if (pos == npos || pos + n >= _size){
    				_str[pos] = '\0';
    				_size = pos;
    			}
    			else{
    				_str[pos] = '\0';
    				strcat(_str + pos, _str + pos + n);
    			}
    		}
    
    		/* String operations: */
    		const char* c_str()const{
    			return _str;
    		}
    
    		size_t find(char ch, size_t pos = 0){
    			for (size_t i = 0; i < _size; ++i){
    				if (ch == _str[i]) return i;
    			}
    			return npos;
    		}
    
    		size_t rfind(char ch, size_t pos = npos){
    			if (pos == npos) pos = _size - 1;
    			for (size_t i = pos; i >= 0; --i){
    				if (ch == _str[i]) return i;
    			}
    			return npos;
    		}
    		string substr(size_t pos = 0, size_t n = npos){
    			if (n == npos) n = _size - pos;
    			char* temp = new char[n + 1];
    			strncpy(temp, _str + pos, n);
    			temp[n] = '\0';
    			return temp;
    		}
    		int compare(const string& s){ strcmp(_str,s._str); }
    		int compare(const char* s){ strcmp(_str, s); }
    
    
    		/* Non-member function overloads */
    		void swap(string&s){
    			std::swap(_str, s._str);
    			std::swap(_size, s._size);
    			std::swap(_capacity, s._capacity);
    		}
    		   // 重载输入输出流运算符时会报参数过多,所以重载成友元或是全局函数
    		friend std::istream& operator>>(std::istream& _cin, string& s){
    			s.clear();
    			char ch = _cin.get();
    			while (ch != '\n' || ch != ' '){
    				s += ch;
    				ch = _cin.get();
    			}
    			return _cin;
    		}
    
    		friend std::ostream& operator<<(std::ostream& _cout, const string& s){
    			_cout << s._str;
    			return _cout;
    		}
    	private:
    		char* _str;
    		size_t _size;     // 有效元素
    		size_t _capacity; // 容量
    
    		/*   vs下维护了长度16的数组,如果长度较小直接使用数组,长度超过在扩容;
    		vs STL与Linux下SGI STL扩容方式不一样     */
    
    		static size_t npos;
    	};
    	size_t string::npos = -1;
    }
    
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
  • 相关阅读:
    银行利率bp是什么意思,基准利率bp是什么意思
    JAVA实现兔子问题--递归
    JDK软件安装+环境变量配置图文详解(Win10环境)
    FS2119A同步升压IC输出3.3V和FS2119B同步升压IC输出5V
    江苏2022农民丰收节 国稻种芯:主场活动在苏中地区泰兴开幕
    深度强化学习+大模型综述Survey on Large Language Model-Enhanced Reinforcement Learning
    钉钉7.5版本多项产品升级,打造年轻人爱用的AI工具
    Java文件操作
    TCR历史期刊为何受大家欢迎?
    Java技术栈学习路线
  • 原文地址:https://blog.csdn.net/weixin_45910068/article/details/126294500