C++中sting类的简单实现方法

String

在C++的学习生涯我中发现String类的功能十分强大,所以我们是很有必要模拟实现它的,况且在面试的时候模拟实现一个String类也是面试官经常会考的,但是因为外界因素的限制我们是不可能模拟的和库里的string一致的(C++库里的string功能更强大),所以今天我们只模拟实现string的基本功能-构造函数,拷贝构造函数,析构函数,赋值运算符重载,运算符+=的重载,运算符[]的重载,c_str(得到一个C风格的字符指针,可操作字符串),Size,Push_Back,Insert(深拷贝),以及用写时拷贝copy_on_write的方式实现基本的String类

深拷贝的方式

class String
{
friend ostream &operator<<(ostream &os,String &s);
public:
String(const char *str=""); //全缺省的构造函数,解决空字符串的问题
String(const String &ps); //深拷贝
String &operator=(String s);
String &operator+=(const char * s);
const char *C_Str()const //得到C风格的字符指针
{
return _pstr;
}
char &operator[](size_t index)
{
return _pstr[index];
}
size_t Size()const
{
return _size;
}
void PushBack(char c);
String &Insert(size_t pos,const char *str);
//String &operator=(String &s)
//{
// cout<<"String &operator=(String &s)"<<endl;
// if(this != &s)
// {
// delete[]_pstr;
// _pstr=new char[strlen(s._pstr)+1];
// strcpy(_pstr,s._pstr);
// }
// return *this;
//}
~String()
{
cout<<"~String()"<<endl;
if(_pstr != NULL)
{
delete[]_pstr;
_pstr=NULL;
_size=0;
_capacity=0;
}
}
private:
void CheckCapacity(int count);
private:
int _size;
int _capacity;
char *_pstr;
};
ostream &operator<<(ostream &os,String &s)
{
os<<s._pstr;
return os;
}
String::String(const char *str)
:_size(strlen(str))
,_capacity(strlen(str)+1)
,_pstr(new char[_capacity])
{
cout<<"String()"<<endl;
strcpy(_pstr,str);
}
String::String(const String &ps)
:_size(ps._size)
,_capacity(strlen(ps._pstr)+1)
,_pstr(new char[_capacity])
{
cout<<"String(const String &ps)"<<endl;
strcpy(_pstr,ps._pstr);
}
String &String::operator=(String s)
{
cout<<"String &operator=(String s)"<<endl;
std::swap(_pstr,s._pstr);
std::swap(_size,s._size);
std::swap(_capacity,s._capacity);
return *this;
}
void String::CheckCapacity(int count)
{
if(_size+count >= _capacity)
{
int _count=(2*_capacity)>(_capacity+count)?(2*_capacity):(_capacity+count);
char *tmp=new char[_count];
strcpy(tmp,_pstr);
delete[]_pstr;
_pstr=tmp;
_capacity=_count;
}
}
void String::PushBack(char c)
{
CheckCapacity(1);
_pstr[_size++]=c;
_pstr[_size]='\0';
}
String &String::operator+=(const char * s)
{
CheckCapacity(strlen(s));
while(*s)
{
_pstr[_size++]=*s;
s++;
}
_pstr[_size]='\0';
return *this;
}
String &String::Insert(size_t pos,const char *str)
{
char *tmp=new char[strlen(_pstr+pos)];
strcpy(tmp,_pstr+pos);
CheckCapacity(strlen(str));
while(*str)
{
_pstr[pos++]=*str;
str++;
}
strcpy(_pstr+pos,tmp);
return *this;
}

通过测试上述代码可正常运行,特别是在实现赋值运算符重载的时候我们使用了两种方式,值得一提的是应用swap函数来实现赋值运算符的重载(在传参时不可以传引用),因为应用swap函数实现是根据临时变量的创建并且该临时变量出作用域就会自动调用析构函数销毁(现代的方法)

测试深拷贝的方法

void text1()
{
String str1("hello");
String str2(str1);
String str3;
str3=str1;
cout<<str1<<endl;
cout<<str2<<endl;
cout<<str3<<endl;
cout<<strlen(str1.C_Str())<<endl; //5
str1[4]='w';
cout<<str1<<endl; //hellw
}
void text2()
{
String str1("abcd");
cout<<str1<<endl;
str1.PushBack('e');
str1.PushBack('f');
str1.PushBack('g');
str1.PushBack('h');
str1.PushBack('i');
cout<<str1<<endl;
cout<<str1.Size()<<endl;
}
void text3()
{
String str1("hello");
String str2("hello world");
String str3(str2);
str1+=" ";
str1+="world";
cout<<str1<<endl;
str2.Insert(6," abc ");
cout<<str2<<endl;
}

实现了深拷贝的方法那仫有没有更加高效的方法呢?当然,那就是写时拷贝,我们发现在上述深拷贝的版本里实现的拷贝构造函数又为新的对象重新开辟空间(防止浅拷贝的后遗症:浅拷贝是值拷贝使得两个指针指向同一块空间,在析构该空间时对同一块空间释放多次就会出现问题),那仫如果我们继承了浅拷贝的后遗症-就让多个指针指向同一块空间,此时我们只需要设置一个指针变量让它记录指向这块空间的指针个数,在析构时只要该指针变量的内容为1我们就释放这块空间否则就让计数器减1,这就是写时拷贝的主要思想,下面就让我们用写时拷贝的方法实现一个简单的String类吧

写时拷贝的方法

//写时拷贝的方式
class String
{
friend ostream& operator<<(ostream & os,String &s);
public:
String(const char *str="")
:_str(new char[strlen(str)+1+4])
{
cout<<"构造"<<endl;
_str+=4;
*((int *)(_str-4))=1;
strcpy(_str,str);
}
String(String &s)
{
cout<<"拷贝构造"<<endl;
++*((int *)(s._str-4));
_str=s._str;
}
String &operator=(const String &s)
{
cout<<"赋值语句"<<endl;
if(--*(int *)(_str-4) == 0)
{
delete[](_str-4);
}
++(*(int *)(s._str-4));
_str=s._str;
return *this;
}
char &operator[](int index) //写时拷贝
{
assert(index >= 0 && index < (int)strlen(_str));
if(*(int *)(_str-4) > 1)
{
--*(int *)(_str-4);
char *tmp=new char[strlen(_str)+5];
strcpy(tmp+4,_str);
delete[](_str-4);
_str=tmp+4;
*(int *)(_str-4)=1;
}
return _str[index];
}
~String()
{
cout<<"析构"<<endl;
if(--*(int *)(_str-4) == 0)
{
cout<<"释放"<<endl;
delete[](_str-4);
}
}
private:
char *_str;
};
ostream& operator<<(ostream &os,String &s)
{
os<<s._str;
return os;
}

在这里我们将指针指向的计数器的位置放置在数据空间的前四个字节处

测试用例:

void test1()
{
String str1("abcd");
cout<<str1<<endl;
String str2(str1);
cout<<str2<<endl;
String str3;
str3=str1;
cout<<str3<<endl;
}
void test2()
{
String str1("abcd");
cout<<str1<<endl;
String str2;
str2=str1;
cout<<str2<<endl;
str2[2]='w';
cout<<str2<<endl;
}

以上所述是小编给大家介绍的C++中sting类的简单实现方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 从string类的实现看C++类的四大函数(面试常见)

    朋友面试的一道面试题,分享给大家,面试官经常会问到的,实现string类的四大基本函数必掌握. 一个C++类一般至少有四大函数,即构造函数.拷贝构造函数.析构函数和赋值函数,一般系统都会默认.但是往往系统默认的并不是我们所期望的,为此我们就有必要自己创造他们.在创造之前必须了解他们的作用和意义,做到有的放矢才能写出有效的函数. #include <iostream> class CString { friend std::ostream & operator<<(std::

  • c++中的string常用函数用法总结

    标准c++中string类函数介绍 注意不是CString之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必 担心内存是否足够.字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要.我们可以用 = 进行赋值操作,== 进行比较,+ 做串联(是不是很简单?).我们尽可以把它看成是C++的基本数据类型. 好了,进入正题---首先,为了在我们的程序中使用string类型,我们必须包含头文件 <string>

  • c++中string类成员函数c_str()的用法

    1.string类成员函数c_str()的原型: const char *c_str()const;//返回一个以null终止的c字符串 2.c_str()函数返回一个指向正规c字符串的指针,内容和string类的本身对象是一样的,通过string类的c_str()函数能够把string对象转换成c中的字符串的样式; 3.操作c_str()函数的返回值时,只能使用c字符串的操作函数,如:strcpy()等函数.因为,string对象可能在使用后被析构函数释放掉,那么你所指向的内容就具有不确定性.

  • c++ String去除头尾空格的方法

    本文实例讲述了c++ String去除头尾空格的方法,分享给大家供大家参考.具体实现方法如下: 实现该功能可使用string的find_first_not_of,和find_last_not_of方法,具体实现带如下: 复制代码 代码如下: #include <iostream> #include <string> std::string& trim(std::string &); int main() {     std::string s = " He

  • C字符串与C++中string的区别详解

    在C++中则把字符串封装成了一种数据类型string,可以直接声明变量并进行赋值等字符串操作.以下是C字符串和C++中string的区别:  C字符串 string对象(C++) 所需的头文件名称  <string>或<string.h> <string>或<string.h> 需要头文件 原因 为了使用字符串函数 为了使用string类 声明 方式 char name[20]; string name; 初始化方式 char name[20]="

  • C++如何通过ostringstream实现任意类型转string

    再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便.后来去网上找了一下,发现有一个好方法: 复制代码 代码如下: #include <iostream>#include <sstream>#include <string>using namespace std; int main(){ int a = 55; double b = 65.123; string str = "&quo

  • 利用C++实现从std::string类型到bool型的转换

    利用输入字符串流:std::istringstream 复制代码 代码如下: bool b;std::string s = "true";std::istringstream(s) >> std::boolalpha >> b; 但当字符串s为"1"时,上面的代码无法正确转换,此时应该用: 复制代码 代码如下: bool b;std::string s = "1";istringstream(s) >> b;

  • 关于C++ string和c类型字符数组的对比

    在c++中string是很方便操作的字符串,支持多种算数运算和比较运算,操作起来非常灵活.string也具有一些容器的性质,可以通过迭代器对字符元素进行访问 c类型的字符数组有如下三种初始化方式: //前两种的初始化方式是等同的,最后一种没有'\0' char *cp="hello"; char c_arr[]="hello"; char c_arr2[]={'h','e','l','l','o'}; 这三种定义c字符串的方式,其中前两种虽然形式不一样但含义是一摸一

  • 用标准c++实现string与各种类型之间的转换

    要实现这个目标,非stringstream类莫属. 这个类在头文件中定义, < sstream>库定义了三种类:istringstream.ostringstream和stringstream,分别用来进行流的输入.输出和输入输出操作.另外,每个类都有一个对应的宽字符集版本. 简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作. 示例1示范怎样使用一个stringstream对象进行从 string到int类型的转换 注意,使用string对象来代替字符数组

  • 详解C++中StringBuilder类的实现及其性能优化

    介绍 经常出现客户端打电话抱怨说:你们的程序慢如蜗牛.你开始检查可能的疑点:文件IO,数据库访问速度,甚至查看web服务. 但是这些可能的疑点都很正常,一点问题都没有. 你使用最顺手的性能分析工具分析,发现瓶颈在于一个小函数,这个函数的作用是将一个长的字符串链表写到一文件中. 你对这个函数做了如下优化:将所有的小字符串连接成一个长的字符串,执行一次文件写入操作,避免成千上万次的小字符串写文件操作. 这个优化只做对了一半. 你先测试大字符串写文件的速度,发现快如闪电.然后你再测试所有字符串拼接的速

  • C++实现string存取二进制数据的方法

    本文实例讲述了C++实现string存取二进制数据的方法,分享给大家供大家参考.具体方法分析如下: 一般来说,STL的string很强大,用起来也感觉很舒服,这段时间在代码中涉及到了用string存取二进制数据的问题,这里记录一下,以供以后参考. 首先提一下STL中string的参考资料:http://www.cplusplus.com/reference/string/string/ ,不懂的朋友可以看下. 在数据传输中,二进制数据的buffer一般用系统预设的大数组进行存储,而不是STL的s

随机推荐