关于string的一些心得体会

要写string的函数,首先就是创建一个string的类,在实现string类的过程中一直不断优化,以减少代码量和考虑不周的问题,首先我先给出刚开始的经典写法

经典解法(初级程序员适用)

class String

{

public:

String(const char *str = ""):       //构造函数

_str(new char[strlen(_str)+1])

{

strcpy(_str, str);

}

String(const String &str)         //拷贝构造函数

{

_str = new char[strlen(str._str) + 1];

strcpy(_str, str._str);

}

~String()                         //析构函数

{

if (_str != NULL)

{

delete[]_str;

}

}

public:

String &operator=(const String &str)//‘=‘符号重载

{

if (this != &str)

{

delete[]_str;

_str = (char*)new char[strlen(str._str) + 1];

strcpy(_str, str._str);

}

return *this;

}

}

此写法看似解决了string类的定义问题,实则仔细查看此代码你会发现代码在开辟空间时并未检测开辟空间是否成功,若new开辟空间失败,_str的内容已经丢失,所以可以看出来好的代码必定是经过深思熟虑后写出来的,在写代码时一定要考虑各种边际情况和各种异常情况的处理,在我们发现上面的问题后作出修改,代码已不够简洁,进过优化后的代码如下:

class String

{

public:

String(char *str = ""):// 构造函数

_str(new char[strlen(str)+1])

{

strcpy(_str, str);

}

//使用swap,一致性

String(String &str) ://拷贝构造函数,注意必须给_str赋初值,否则_str是随机值,指向的是一块非法空间

_str(NULL)

{

String tmp(str._str);

swap(_str, tmp._str);

}

String &operator=(String str)// ‘=‘号重载,巧妙使用值传递,在函数结束时,调用析构函数,将_str之前的空间释放

{

swap(_str, str._str);

return *this;

}

~String()                         //析构函数

{

if (_str != NULL)

{

delete[]_str;

}

}

在上边的代码中,我们发现在使用swap后代码变得简洁,而且在‘=‘符号重载时也不会出现丢失_str内容的情况,特别注意在拷贝构造中,一定要给_str赋初值,否则在代码块结束会出现释放非法空间的情况,因为在_str未赋初值的情况下是一个随机值,也就是_str指向一块非法空间。

此时的写法依然不是最好的写法,因为在程序运行的过程中使用动态开辟所占用的时间的比较多,所以此时就有写时拷贝的概念:

在维基百科中是这么定义的,写入时复制(Copy-on-write)是一个被使用在程式设计领域的最佳化策略。其基础的观念是,如果有多个呼叫者(callers)同时要求相同资源,他们会共同取得相同的指标指向相同的资源,直到某个呼叫者(caller)尝试修改资源时,系统才会真正复制一个副本(private copy)给该呼叫者,以避免被修改的资源被直接察觉到,这过程对其他的呼叫只都是通透的(transparently)。此作法主要的优点是如果呼叫者并没有修改该资源,就不会有副本(private copy)被建立。

简单的来说,就是在程序运行的过程中,如果多个对象同时要求相同的内容,则系统会给他们指定相同的空间,直至其中一个对象需要修改内容时才会真正给这个对象复制一个副本,从而提高程序的运行效率。

代码如下:

class String            //写时拷贝

{

public:

String(const char* str = ""):

_str(new char[strlen(str) + 1 + CAPACITY+4])     //多开辟四个字节存放空间使用次数

, _capacity(strlen(str) + 1 + CAPACITY)

, _size(strlen(str))

{

*(int *)_str = 1;

_str = _str + 4;

strcpy(_str, str);

}

String(const String &str)

:_str(str._str)

{

++*(PointFirst(_str));

_capacity = str._capacity;

_size = str._size;

}

~String()

{

if (--*PointFirst(_str) == 0)      //当使用相同空间的对象最后一次析构时,释放空间

{

delete[](_str - 4);

}

}

}
上面的代码和前面的不同处是在开辟空间时多开辟了四个字节去保存空间的使用次数,每次拷贝构造时加一,在析构时也是在保存的这个值为0时才会释放空间。

我们验证下写实拷贝和经典拷贝的效率差异

void Test()

{

String str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

int i = 0;

time_t start = time(NULL);

for (; i < 20000000; i++)

{

String stri(str);

}

time_t end = time(NULL);

cout << (end - start) << endl;

}//使用普通拷贝构造函数,用时25s,使用写实拷贝只需要3s,效率一目了然。

在下面我再将自己写的一些使用写实拷贝的一些简单的string函数贴出来:
代码如下:

public:

String &operator=(const String &str)

{

if (this != &str)

{

if (--(*PointFirst(_str)) == 0)     //当此对象独立使用一块空间时,在赋值前释放这块空间

{

delete[]PointFirst(_str);

}

_str = str._str;

++*PointFirst(str._str);

}

return *this;

}

char &operator[](int index)

{

if ((*PointFirst(_str)-1) == 0)

{

return _str[index];

}

--*PointFirst(_str);

StringCopyWrite();

return _str[index];

}

public:

int Strcmp(const String &str)

{

char *pstr1 = _str;

char *pstr2 = str._str;

while (*pstr1 == *pstr2)

{

pstr1++;

pstr2++;

if (*pstr1 == ‘\0‘&&*pstr2 == ‘\0‘)

{

return 0;

}

}

if (*pstr1 > *pstr2)

{

return 1;

}

return -1;

}

bool operator==(const String &str)

{

if (Strcmp(str) == 0)

{

return true;

}

return false;

}

bool operator>(const String &str)

{

if (Strcmp(str) == 1)

{

return true;

}

return false;

}

bool operator<(const String &str)

{

if (Strcmp(str) == -1)

{

return true;

}

return false;

}

//写时拷贝函数

void insert(int pos, char ch)

{

if (--*PointFirst(_str) != 0)  //和其他类成员变量公用一块空间

{

StringCopyWrite();

}

CheckCapacity(Strlen(_str) + 2);

int index = _size;

int sz = Strlen(_str);

while (sz >= pos)

{

_str[index - 1] = _str[sz];

index--;

sz--;

}

_str[pos] = ch;

}

void insert(int pos, char *str)

{

if (--*PointFirst(_str) != 0)

{

StringCopyWrite();

}

CheckCapacity(Strlen(_str) + Strlen(str) + 1);

int sz = Strlen(_str);

int index = _size;

while (sz >= pos)

{

_str[index - 1] = _str[sz];

index--;

sz--;

}

while (*str != ‘\0‘)

{

_str[pos++] = *str++;

}

}

void insert(int pos, const String &str)

{

if (--*PointFirst(_str) != 0)

{

StringCopyWrite();

}

this->insert(pos, str._str);

}

void PushBack(char ch)

{

if (--*PointFirst(_str) != 0)

{

StringCopyWrite();

}

int sz = size();

this->insert(sz - 1, ch);

}

int size()//返回值即为字符串长度(包含‘\0‘)

{

char *p = _str;

int size = 0;

while (*p != ‘\0‘)

{

size++;

p++;

}

return ++size;

}

void CheckCapacity(int sz)

{

if (sz >= _capacity)

{

_capacity = sz + CAPACITY;

}

_size = sz;

char *tmp = new char[_capacity + 4];

*(int *)tmp = *PointFirst(_str);

tmp += 4;

Strcpy(tmp, _str);

swap(tmp, _str);

delete[]PointFirst(tmp);

}

void StringCopyWrite()

{

char *tmp = _str;

_str = new char[Strlen(_str) + 5];

_str += 4;

Strcpy(_str, tmp);

*PointFirst(_str) = 1;

}

private:

char *_str;

int _size;

int _capacity;

};

在使用动态开辟的程序中,非常容易造成内存泄漏,很多程序的问题都处在释放动态内存处,这也是c++中比较难的一个知识点,只有能熟练使用动态内存开辟,才能将c++的魅力体现出来。

随后再更........

时间: 2024-12-04 18:32:08

关于string的一些心得体会的相关文章

关于Solr的使用总结的心得体会

摘要:在项目中使用Solr作为搜索引擎对大数据量创建索引,提供服务,本文是作者对Solr的使用总结的一点心得体会, 具体包括使用DataImportHandler从数据库中近实时同步数据.测试Solr创建索引的性能.以及测试Solr的搜索效率总结等. 具体搜索引擎概念.Solr搭建方法.数据库mysql使用方法,假设读者已有了基础.本文操作均是在linux上进行的. 1.      Solr 1.1  Solr从数据库中读取数据并创建索引速度(使用DataImportHandler) l 一次性

关于Tomcat 和 Spring 整合 WebSocket 的心得体会 (一)

卤煮最近在做一个项目,需要用到服务器与页面进行通讯. 常见的ajax循环检测(轮询)对服务器压力较大,于是组长建议使用WebSocket技术,即长连接. 这里记录一下搭建WebSocket运行环境的心得体会,以飨大家. 运行环境: Windows System 7, Eclipse Mars 3.1, Tomcat 7.0.48/7.0.69, spring 4.0.2 最开始使用的是Tomcat7.0.48 搭配 servlet用. 需要建立两个类 分别继承WebSocketServlet 和

第四章学习小结 串的模式匹配 解题心得体会

串的模式匹配 解题心得体会 关于串,模式匹配是其一个很重要的问题.针对这个问题,书上讲了两种模式匹配的算法,即BF算法和KMP算法,下面针对这两种算法的实现谈谈我的心得. 一.BF算法的探索 [错误代码1] #include<iostream> #include<string.h> using namespace std; typedef struct{ char ch[1000002]; int length; }SString; void Index_BF(SString S,

AngularJS心得体会

AngularJS早些时候有过了解,知道这是一个JS的MVC框架,同类型的框架还有Backbone等.这次是由于项目需要,学习了两天的Angular后开始着手改之前的项目代码,这里大概说一下这一周学习Angular的心得体会吧. 首相,使用Angular最大的感受就是它的设计思路完全不同于Jquery,jquery更倾向于对Dom的操作:而使用Angular则需要你有一个全局的认识,你必须知道你想要做成什么样子才可以下手去做,所以我感觉ng对前端开发的要求比jquery要高一些.先来看看Angu

关于软件项目管理的心得体会之一

目的 软件项目管理是一项涉及面较广,但是非常必要的一项技能.相较于软件开发中的其他专业技能, 又更加依赖于实践和阅历.这里想跟各位同仁分享一下自己在过往项目中的心得体会,结合些许耳熟能详的理论,起到抛砖引玉的作用. 局限性 项目管理既然是一门实践科学,所以这里跟大家分享之前,还是要说明局限性.因为我之前是在一家提供软件服务的传统软件公司工作, 所以很多项目的经验都来源于作为乙方的外包项目,同时,大部分项目都是移动相关领域.目前我在一家国内的互联网公司,从事的电商相关的应用项目. 开篇 想跟大家分

编程方法心得体会

编程学习心得和方法(转载,供新手参考)   即便是高级编程人员,像那些为开发软件工作的类库和组件高手,他们也不敢说自己精通每一种语言. 事实就是这样,每一种语言有着它们相似而又不同的语法,它们有着不同的关键字,发展出了各种各样的类库,它们的函数或者方法的声明定义都有不同,更多的差异不为我们所知. 每一种语言,它都有着太多的东西需要我们去理解和记忆,我们没有时间和精力做得太多. 对于初学者,我们最不应该急于求成,更不要试图一次性学习所有语言.事实上,我们可以选取某种语言学习.每一种语言都有着自己的

读《构建之法》的心得体会

读<构建之法>的心得体会 软件工程涉及的范围很广,对于即将投身IT业的学生而言,软件工程的内容又非常重要.读构建之法,尽管本书介绍了不少IT业正在使用的理论和技术,但是,这本书的主要思想并不是介绍所有的新思想和新技术,而是从这些新思想.新技术中总结出对自己在未来的工作中有用的东西. 在整本书中,印象最让我深刻的是“两个人的合作”这一章节.现代的软件产业经过几十年的发展,软件的结构随着用户需求的不断增加,软件的功能不断朝多元化与复杂化发展.不管是两个人的合作还是团队的合作,谈到合作不免提及规范这

Git的基本使用方法和安装&amp;心得体会

1. git的安装和github的注册.代码托管.创建organization.邀请member. (1)git的安装 因为我电脑是windows系统,所以下载的是git for windows.在官网下载非常卡,直接百度搜找百度那个下载就可以.下载后选择路径一直next就行了. (2)github的注册 没什么说的,虽然界面是英文,不过要是连这种程度的都看不懂你还是洗洗睡吧.按要求填完邮箱账号密码等常规东西就注册完了. (3)创建organization the organization's

IT增值服务实践心得体会:企业客户的钱比个人客户好赚得多

友情提示 本人喜欢直言不讳,不喜欢拐弯抹角.喜欢从客观和主观.自身和他人等多种角度去探讨问题.如有不当之处,欢迎吐槽. 若干心得体会1.企业客户的钱更好赚,个人客户的钱很难.  为什么这么说呢? a.企业有钱赚,为了更好地服务自己的客户,赚到更多的钱,花点钱,心甘情愿.只要你的商品和服务,不至于太坑. b.企业的功利心比较强,因此,非常有目标性,有动力,一定要解决自己遇到的问题. c.个人的付费意识很差,因为他们总认为自己赚的钱少,同时下意识地认为,别人花时间精力提供的服务或者劳动成果,不值钱.