1.string淺拷貝的問題
// 為了和標準庫區分,此處使用String
class String
{
public :/*String():_str(new char[1]){*_str = '\0';}*///String(const char* str = "\0") // 錯誤示范//String(const char* str = nullptr) // 錯誤示范String(const char* str = ""){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;
};int main()
{String s1("hello world!");String s2(s1); // 這里會析構兩次,導致程序崩潰return 0;
}
說明:
????????上述String類沒有顯式定義其拷貝構造函數與賦值運算符重載,此時編譯器會合成默認的,當用s1構造s2時,編譯器會調用默認的拷貝構造。最終導致的問題是,s1、s2共用同一塊內存空間,在釋放時同一塊空間被釋放多次而引起程序崩潰,這種拷貝方式,稱為淺拷貝。
淺拷貝:
????????也稱位拷貝,編譯器只是將對象中的值拷貝過來。如果對象中管理資源,最后就會導致多個對象共享同一份資源,當一個對象銷毀時就會將該資源釋放掉,而此時另一些對象不知道該資源已經被釋放,以為還有效,所以當繼續對資源進項操作時,就會發生發生了訪問違規。
????????可以采用深拷貝解決淺拷貝問題,即:每個對象都有一份獨立的資源,不要和其他對象共享。
2.深拷貝
????????如果一個類中涉及到資源的管理,其拷貝構造函數、賦值運算符重載以及析構函數必須要顯式給出。一般情況都是按照深拷貝方式提供。
2.1?傳統寫法的String類
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(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;
};
2.1?現代寫法的String類
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=(String s){std::swap(_str, s._str);return *this;}傳統版本//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.string類的模擬實現
頭文件 string.h:
namespace room
{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]{'\0'}),_size(0),_capacity(0){}*/void swap(string& s);string(size_t n, char ch);string(const char* str = "");// s2(s1)string(const string& s);// s1 = s2// s1 = s1//string& operator=(const string& s);// s1 = s2string& operator=(string s);~string();void clear(){_str[0] = '\0';_size = 0;}const char* c_str() const{return _str;}void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);void insert(size_t pos, size_t n, char ch);void insert(size_t pos, const char* ch);void erase(size_t pos = 0, size_t len = npos);size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);size_t size() const{return _size;}size_t capacity() const{return _size;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}string substr(size_t pos, size_t len = npos);bool operator==(const string& s) const;bool operator!=(const string& s) const;bool operator<(const string& s) const;bool operator<=(const string& s) const;bool operator>(const string& s) const;bool operator>=(const string& s) const;private:// 聲明char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;const static size_t npos;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);istream& getline(istream& in, string& s, char delim);
}
源文件string.cpp:
// 鏈接時會合并
namespace room
{const size_t string::npos = -1;string::string(size_t n, char ch):_str(new char[n + 1]),_size(n),_capacity(n){for (size_t i = 0; i < n; ++i){_str[i] = ch;}_str[_size] = '\0';}string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}傳統寫法s2(s1)//string::string(const string& s)//{// _str = new char[s._capacity + 1];// strcpy(_str, s._str);// _size = s._size;// _capacity = s._capacity;//}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 現代寫法// s2(s1)string::string(const string& s){string tmp(s._str);swap(tmp);}// s1 = s2// s1 = s1/*string& string::operator=(const string& s){if (this != &s){delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}*/// s1 = s2string& string::operator=(string s){swap(s);return *this;}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char ch){if (_size + 1 > _capacity){// 擴容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0'; // 末尾得加上一個\0}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){// 擴容size_t newCapacity = 2 * _capacity;if(_size + len > 2 * _capacity){newCapacity = _size + len;}reserve(newCapacity);}strcpy(_str + _size, str);_size += len;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::insert(size_t pos, size_t n, char ch){assert(pos <= _size);assert(n > 0);if (_size + n > _capacity){// 擴容size_t newCapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newCapacity = _size + n;}reserve(newCapacity);}// 挪動數據// 這樣挪動數據,頭插的時候會越界/*size_t end = _size;while (end >= pos){_str[end + n] = _str[end];--end;}*/size_t end = _size + n;while (end > pos + n - 1){_str[end] = _str[end - n];--end;}for (size_t i = 0; i < n; ++i){_str[pos + i] = ch;}_size += n;/*string tmp(n, ch);insert(pos, tmp.c_str());*/}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t n = strlen(str);if (_size + n > _capacity){// 擴容size_t newCapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newCapacity = _size + n;}reserve(newCapacity);}size_t end = _size + n;while (end > pos + n - 1){_str[end] = _str[end - n];--end;}for (size_t i = 0; i < n; ++i){_str[pos + i] = str[i];}}void string::erase(size_t pos, size_t len){if (len >= _size - pos){// 刪完數據_str[pos] = '\0';_size = pos;}else{size_t end = pos + len;while (end <= _size){_str[end - len] = _str[end];++end;}_size -= len;}}size_t string::find(char ch, size_t pos){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){const char* p = strstr(_str + pos, str);if (p == nullptr){return npos;}else{return p - _str;}}string string::substr(size_t pos, size_t len){size_t leftlen = _size - pos;// 給的長度大于剩余的長度時,len等于剩余長度if (len > leftlen)len = leftlen;string tmp;tmp.reserve(len);for (size_t i = 0; i < len; ++i){tmp += _str[pos + i];}return tmp;}bool string::operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s) const{return !(*this == s);}bool string::operator<(const string& s) const{return strcmp(_str, s._str) < 0;}bool string::operator<=(const string& s) const{return *this < s || *this == s;}bool string::operator>(const string& s) const{return !(*this <= s);}bool string::operator>=(const string& s) const{return !(*this < s);}ostream& operator<<(ostream& out, const string& s){for (auto ch : s)out << ch;return out;}istream& operator>>(istream& in, string& s){s.clear();// 輸入短串,不會浪費空間// 輸入長串,避免不斷擴容const size_t N = 1024;char buff[N];int i = 0;//cin >> i; // 這樣是不行的char ch = in.get(); // 用get()才能收到空格和換行// 短串就放入buffwhile (ch != ' ' && ch != '\n') // 遇到\n就結束{buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){// 長串才擴容,減小擴容帶來的性能消耗buff[i] = '\0';s += buff;}return in;}istream& getline(istream& in, string& s, char delim){s.clear();// 輸入短串,不會浪費空間// 輸入長串,避免不斷擴容const size_t N = 1024;char buff[N];int i = 0;//cin >> i; // 這樣是不行的char ch = in.get(); // 用get()才能收到空格和換行// 短串就放入buffwhile (ch != delim) // 遇到指定字符delim就結束{buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){// 長串才擴容,減小擴容帶來的性能消耗buff[i] = '\0';s += buff;}return in;}
}
完