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
原文:http://10743407.blog.51cto.com/10733407/1746156