C++實現string類是理解類和對象這個概念的基礎,也能瞭解C++語法的特性--用戶對內存的自主管理,通過類創建出一個對象的過程,首先要調用構造函數,經過一系列的操作,當退出對象所在的作用域時,便會調用析構函數,C++支持隱式的調用構造、析構等函數,但經常隱式調用並不能正確的管理內存,所以我們需要重寫這兩個函數,以及拷貝構造,和賦值運算符的重載。
string類的結構設計
string類型的設計源於我們隊字符串操作及封裝的需求,所以string類是對C語言中的字符串進行優化故設計代碼如下:
class String
{
private:
char*_str;
};
通過指針管理動態內存所開闢的空間,然後進行所需要的操作即可。
string隱式調用會存在什麼問題
如果我用用以上結構實現string類的話,我們就會用到動態內存開闢來實現字符串的創建,在這裏我們用new來實現string類的構造函數用以創建新的字符串,對其驚醒操作時會出現以下問題:
1.構造新的字符串時我們的構造函數並不能直接給字符串賦值。
2.調用析構函數時,因爲在棧上開闢的僅僅是一個指針,如果不能對廢棄空間進行合理的 釋放,那麼會出現內存泄漏問題。
3.如果重寫析構函數,讓其功能爲對_str的釋放,當我們進行拷貝構造及賦值運算符的重載時會導致一塊空間被釋放多次從而崩潰。
解決方案
1.構造函數開闢空間,使用strcpy進行拷貝,不直接賦值。析構函數對字符指針進行空間釋放,拷貝構造以及賦值運算符的重載另外開闢一塊空間,對指針所指向的內容進行拷貝。(深拷貝)
2.構造函數開闢空間,使用strcpy進行拷貝,不直接賦值。創建一個數據管理同一塊空間被引用的次數(引用計數),若計數爲1,析構函數對其空間進行釋放,若不爲1,計數自減一次。(淺拷貝)
代碼實現
#define _CRT_SECURE_NO_WARNINGS 1//深拷貝
#include <iostream>
#include <cassert>
using namespace std;
class String
{
public:
String(const char*s ="");
~String();
/*String(const String&s)
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
size = s.size;;
capacity = s.capacity;
}*/
String(const String &s)
:_str(NULL)
{
String tmp(s._str);
std::swap(tmp._str, _str);
std::swap(tmp.size, size);
std::swap(tmp.capacity, capacity);
}
String PushBack(const String &s)
{
return *this = *this + s;
}
int PushFind(char goal)
{
assert(_str);
int i = 0;
while (char tmp=*(_str+i))
{
if (tmp == goal)
return i;
i++;
}
return -1;
}
char* C_str()
{
return _str;
}
/*String& operator=(const String &s)//傳統寫法
{
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[]_str;
_str = tmp;
size = s.size;
capacity = s.capacity;
}
return *this;
}*/
String& operator=(String s)//現代寫法
{
std::swap(s._str, _str);
std::swap(s.size, size);
std::swap(s.capacity, capacity);
return *this;
}
String operator + (const String &s)
{
String tmp(this->_str);
while(tmp.size + s.size-1 > tmp.capacity)
{
tmp.capacity *= 2;
tmp._str = (char*)realloc(tmp._str, capacity);
}
strcpy(tmp._str + tmp.size - 1, s._str);
tmp.size = tmp.size + s.size - 1;
return tmp;
}
String operator - (const String &s)
{
size_t i = 0;
String tmp(*this);
char*Cat_Dest = my_strstr(tmp._str, s._str);
for (; i <= (strlen(Cat_Dest) - strlen(s._str)); ++i)
{
*(Cat_Dest + i) = *(Cat_Dest + strlen(s._str) + i);
}
return tmp;
}
char & operator [] (const size_t i)const
{
assert(i <= strlen(_str));
return _str[i];
}
private:
char* my_strstr(char*str, char*sub)
{
assert(str);
assert(sub);
char*Str_tail = str;
char*Hstr = str;
char*Hsub = sub;
while (*Str_tail)
Str_tail++;
if (!(*sub)) //處理減去空串,若爲子串空串,則返回父串尾
return Str_tail;
while ((*str)&&(*sub)//處理非空串
&&(strlen(Hstr)>=strlen(Hsub)))
{
str = Hstr++;
sub = Hsub;
while (*sub == *str
&&(*sub))
{
sub++;
str++;
}
}
if (*sub)
return Str_tail;//若不爲子串,則返回父串尾
return Hstr-1; //若爲子串則返回子串在父串中所在位置
}
char *_str;
size_t size;
size_t capacity;
};
String::String(const char*s)
:_str(new char[strlen(s)+1])
{
strcpy(_str, s);
size = strlen(s) + 1;
capacity = size * 2;
}
String::~String()
{
if (_str)
{
delete[] _str;
_str = NULL;
capacity = 0;
size = 0;
}
}
#include<iostream>//淺拷貝
class String
{
public:
String(char*str)
:_str(new char[strlen(str)+1])//此外淺拷貝還可以將_RefCount與_str一併開闢
{//通過強制類型轉換操作同一塊空間減少內存碎片的操作
strcpy(_str,str);
*_RefCount = 1;
}
~String()
{
if (--(*_RefCount) == 0)
delete[]_str;
}
String(const String& s)
{
if (_str != s._str)
{
_RefCount = s._RefCount;
_str = s._str;
++(*_RefCount);
}
}
String operator = (String &s)
{
if (_str != s._str)
{
if (--(*_RefCount) == 0)
delete[]_str;
_str = s._str;
_RefCount = s._RefCount;
++(*_RefCount);
}
}
private:
char* _str;
int* _RefCount;
};
如有錯誤,希望批評指正。
本文出自 “pawnsir的IT之路” 博客,請務必保留此出處http://10743407.blog.51cto.com/10733407/1746156