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

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类的简单实现方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c
string类实现
c语言实现简单数据库、链表c语言实现 简单、sting算法实现、哈夫曼编码c语言实现、c语言接口与实现 pdf,以便于您获取更多的相关知识。

时间: 2024-09-20 08:00:41

C++中sting类的简单实现方法_C 语言的相关文章

C++将CBitmap类中的图像保存到文件的方法_C 语言

本文实例讲述了C++将CBitmap类中的图像保存到文件的方法.分享给大家供大家参考.具体实现方法如下: 使用下面的代码,可以把CBitmap类中的图像保存到图像文件中.支持格式:BMP.JPG.GIF和PNG. void SaveBitmap(CString strFilePath, CBitmap Bitmap) { if ( Bitmap.m_hObject ) { CImage imgTemp; // CImage是MFC中的类. imgTemp.Attach(Bitmap.operat

Visual C++中Tab View的多种实现方法_C 语言

本文实例讲述了Visual C++中Tab View的多种实现方法,分享给大家供大家参考.具体如下: 一.引言 标签控件(Tab Control)是VC++编程中经常使用的控件之一,它允许在单个对话框或窗口中设置多个页面,每个页面代表一组控件.当某个页面的标签被选中时,该页 面内的控件就会被显示出来.标签控件使得在有限的窗口空间内可以显示更多的信息,而且分类清晰.同时,VC++提供了以文档/视图 (Document/View)结构方式开发应用程序的简单方法,在文档中保存数据,在视图中显示数据.无

C++线程池的简单实现方法_C 语言

本文以实例形式较为详细的讲述了C++线程池的简单实现方法.分享给大家供大家参考之用.具体方法如下: 一.几个基本的线程函数: 1.线程操纵函数: int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void*)(*start_rtn)(void *), void *arg); //创建 void pthread_exit(void *retval); //终止自身 int pthread_cancel(pthread_

浅谈C++中派生类对象的内存布局_C 语言

主要从三个方面来讲: 1 单一继承 2 多重继承 3 虚拟继承 1 单一继承 (1)派生类完全拥有基类的内存布局,并保证其完整性. 派生类可以看作是完整的基类的Object再加上派生类自己的Object.如果基类中没有虚成员函数,那么派生类与具有相同功能的非派生类将不带来任何性能上的差异.另外,一定要保证基类的完整性.实际内存布局由编译器自己决定,VS里,把虚指针放在最前边,接着是基类的Object,最后是派生类自己的object.举个栗子: class A { int b; char c; }

VC中删除类的两种操作方法_C 语言

本文实例讲述了VC中删除类的两种操作方法.分享给大家供大家参考.具体方法如下: 方法一: 应该是先在windows里面删除.h和.cpp文件,然后打开项目,Ctrl-W打开Class    wizard,选这个Dialog类,然后提示找不到了,选remove,然后到File    view列表里面选这两个文件,按Delete删除,这样才彻底,否则.clw文件内会保留信息的 方法二: 在你的工程的FileView中删除相应的h文件和cpp文件,然后把工程关了 到相应文件夹下,把该类的h和cpp文件

c++ 构造函数中调用虚函数的实现方法_C 语言

我们知道:C++中的多态使得可以根据对象的真实类型(动态类型)调用不同的虚函数.这种调用都是对象已经构建完成的情况.那如果在构造函数中调用虚函数,会怎么样呢? 有这么一段代码: class A { public: A ():m_iVal(0){test();} virtual void func() { std::cout<<m_iVal<<' ';} void test(){func();} public: int m_iVal; }; class B : public A {

c语言中字符串分割函数及实现方法_C 语言

1.问题引入 自己在写一个linux下的模拟执行指令的时候,遇到了输入"cat a.c",要将该字符串分解成cat和a.c两个单独的字符串,虽然知道有strtok的存在,但是想自己尝试写一下,于是就自己写了一个,不过总是遇到这样或那样的问题,虽然最后调通了,不过确浪费了不少时间:后来作业交上去以后又仔细阅读了strtok函数,发现原来linux下已经改成strsep,所有在这里就写一下自己所走的过程. 2.自己写的字符串分割函数:用于分割指令,比如cat a.c最后会被分割成cat和a

C++设置超时时间的简单实现方法_C 语言

本文实例讲述了C++设置超时时间的简单实现方法,代码简单易懂,功能实用.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: BOOL SetTimeOut(SOCKET s, int nTime, BOOL bRecv)  {      int ret = ::setsockopt(s, SOL_SOCKET, bRecv?SO_RCVTIMEO:SO_SNDTIMEO, (char*)nTime, sizeof(nTime));      return ret!=SOCKET_ER

解读C++编程中派生类的构成和创建_C 语言

C++派生类的构成 派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分.从基类继承的成员体现了派生类从基类继承而获得的共性,而新增加的成员体现了派生类的个性.正是这些新增加的成员体现了派生类与基类的不同,体现了不同派生类之间的区别. 在基类中包括数据成员和成员函数 (或称数据与方法)两部分,派生类分为两大部分:一部分是从基类继承来的成员,另一部分是在声明派生类时增加的部分.每一部分均分别包括数据成员和成员函数. 实际上,并不是把基类的成员和派生类自己增加的成员简单地加在一起就成为派生