柔性数组结构体设计:

Struct Node 用于模拟String

struct StrNode  //均可以在构造对象时使用{int  ref;  //引用计数int len;    //字符串长度int size;   //空间大小char data[];   //柔性数组};

类的设计

StrNode作为类的私有成员(注,StrNode属于类型)

使用StrNode类型的指针pstr,作为访问结构体中的成员接口。

class String
{private:struct StrNode  //均可以在构造对象时使用{int  ref;  //引用计数int len;    //字符串长度int size;   //空间大小char data[];   //柔性数组};  StrNode* pstr;  //通过str来访问StrNode中的成员

构造函数设计

len:是要传入data[]的字符串长度
sizeof(StrNode):计算的是结构体类型总字节的偏移量。

public://构造函数String(const char* p = NULL) :pstr(NULL){if (p != NULL){int len = strlen(p);//pstr申请堆空间存放ref,len,size,data[]pstr = (StrNode*)malloc(sizeof(StrNode) + len * 2 + 1);pstr->ref = 1; //引用计数,表示此时有一个对象pstr->len = len; //传给data 的字符串长度pstr->size = len * 2;//data中的容量大小strcpy_s(pstr->data, len+1, p);}cout << "create construct " << this << endl;}

析构函数设计

1.先判断pstr是否为空。ptr中有如下四个成员

2.

注意:

(如果ref不为1,说明有一个以上的对象指向pstr,不能对pstr指向的堆空间进行释放,只需要置为空,断开当前对象和pstr的联系,只能等只剩下一个对象,在对空间进行释放。见可以防止内存泄漏)
对对象个数减一,确定没有对象指向该字符串。则把该对象内的ptr中申请的堆空间释放,并置为空。

~String()
{     //首先判断pstr是否为空,不为空,就对ref-1if (pstr != NULL && --pstr->ref == 0){free(pstr);}//否则pstr的指向均为空,或没有对象cout << "destory " << this << endl;pstr = NULL;}
};
int main()
{String s1{ "baiU" };//String s2(s1);
}

拷贝构造函数设计

  1. 先将s1中的ptr置为空
  2. 将s1中ptr指向的地址空间传给s2
  3. 然后s2对应的ref+1
    如下图:
//拷贝构造String(const String& s) :pstr(NULL){                       //先将pstr置为空if (s.pstr != NULL){pstr = s.pstr;pstr->ref += 1;}cout << "copy consruct" << this << endl;}


所以s1和s2中pstr指向同一空间。

赋值运算符重载

1.首先判断对象s1等于对象s2
2.不相等,则在判断s1中ptr是否为空,不为空则将对象减一,然后将ptr指向的堆空间释放。
3.让s1中的ptr指向s2中的ptr。
4.如果s1ptr不为空了,在对引用计数加一
5.返回this指针。

String& operator=(const String& s){   //先判断this指向的pstr中的data是否和s的字符串中的内容相等if (this != &s){//再次判断pst是否为空和对象个数if (pstr != NULL && --pstr->ref == 0){free(pstr);}pstr = s.pstr;if (pstr != NULL){pstr->ref += 1;}}cout << "operator =" << this << endl;return *this;}
int main()
{String s1{ "baiU" };//String s2(s1);String s3("shiqianyu");s3 = s1;
}

String s1{ "baiU" };
String s3("shiqianyu");

s3 = s1;

括号运算符重载

使用原因: 希望可以通过[]运算符来改变对象字符串的某个元素

int main()
{String s1{ "baiU" };String s2(s1);s1[1] = 's';
}

如果有多个对象指向pstr,也就是指向同一个字符串,那么改变s1,就会改变s2的值,这个时候,我们就需要进行写时拷贝

写时拷贝

1.先判断pstr是否为空,如果为空,说明没有申请空间,直接退出。
2.判断输入的下标,是否在下标范围内。
3.判断引用计数是否大于一,如果不大于直接返回下标元素,大于,说明还有其他对象,指向该字符串,需要进行拷贝。
4.重新申请堆空间,将内容拷贝到新的堆区
5.将新空间的引用计数加一,原来空间的引用计数减一,最后将新空间赋给该对象的指针pstr

char& operator[](const int index){if (pstr == NULL) exit(1);assert(index >= 0 && index <= pstr->len - 1);if (pstr->ref > 1){int total = sizeof(StrNode) + pstr->size + 1;StrNode* newNode = (StrNode*)malloc(total);memcpy(newNode, pstr, total);newNode->ref = 1;pstr->ref = 1;pstr = newNode;}return pstr->data[index];}

只有一个对象指向pstr

return pstr->data[index];
int main()
{String s1{ "baiU" };s1[1] = 's';
}

重载[]运算符2

如果我们不希望这个运算符是双向的,既可以利用它来取值,又可以利用它来赋值,那么就不需要返回一个引用,也不用写实拷贝。

char operator [](const int index)const{assert(index >= 0 && index <= pstr->size - 1);return pstr->data[index];}

修改某个下标的元素

int main()
{String s1{ "baiU" };//String s2(s1);//String s3("shiqianyu");//s3 = s1;//s1[1] = 's';s1.modify(0, 'a');
}

当我们只需要数据,而不需要获取,我们直接写一个修改函数就可。
原理和写实拷贝基本相同。

bool modify(const int index,const char ch)
{if (pstr == NULL) exit(1);assert(index >= 0 && index <= pstr->len - 1);if (pstr->ref > 1){ //说明有多个对象指向同一个结点。int total = sizeof(StrNode) + pstr->size + 1;StrNode* newNode = (StrNode*)malloc(total);memcpy(newNode, pstr, total);newNode->ref = 1;pstr->ref = 1;pstr = newNode;}pstr->data[index] = ch;return  true;
}

移动构造函数,移动赋值

  1. 移动构造,就是将自己的资源进行转移,转移到要构造的对象里。
  2. 移动赋值,将自己的资源赋值给目标对象,自身置为NULL。
//移动构造函数String(String&& s) :pstr(NULL)  //不需要创建临时对象。{                       //先将pstr置为空pstr = s.pstr;s.pstr =NULL;cout << "move consruct" << this << endl;}
//移动赋值String& operator=(String&& s)  //对对象本身进行操作{   //先判断this指向的pstr中的data是否和s的字符串中的内容相等if (this == &s) return *this;//再次判断pst是否为空和对象个数if (pstr != NULL && --pstr->ref == 0){free(pstr);}pstr = s.pstr;  //让s1的ptr指向s2指向的ptrs.pstr = NULL; //让s2断开cout << "operator =" << this << endl;return *this;}
String fun()
{String s2("shiqinayu");return s2;
}
int main()
{String s1{ "baiU" };s1 = fun();
}

1.对s1初始化

2. 调用构造函数对s2初始化

3. return s2,优先调用移动构造函数,构造将亡值对象,让将亡值对象的ptr指向s2的ptr指向的空间,然后将s2中的ptr置为空。

4. 析构s2

  1. 接下来返回到调用点出,调用移动赋值,直接将s2的堆区空间释放,然后让s1的ptr指向,将亡值对象ptr指向的空间。然后将不具名对象ptr置为空即可完成赋值。

加法运算符重载

  1. 如果两个对象都空,则直接返回空String()/
  2. 如果s1为空,s2不为空,则返回s2.
  3. 如果s1不为空,s2为空,则返回s1
  4. 如果s1和s2都不为空,则需要申请一个新的指针,开辟一段新的堆空间,将两个字符串放进去。
    5.最后返回时,需要调用构造函数,创建一个新对象。(因为是将对象赋值给对象),为了防止调用上面的构造函数,所以需要创建一个特殊的的构造函数。
private:  //这样才能访问strNode
String(StrNode* p):pstr(p){}
//加法运算符重载String operator+(const String& s)const{if (pstr == NULL && s.pstr == NULL)return String();else if (pstr != NULL && s.pstr == NULL)return *this;else if (pstr == NULL && s.pstr != NULL)return  s;else{int total = (pstr->len + s.pstr->len) * 2;StrNode* newNode = (StrNode*)malloc(total + 1);strcpy_s(newNode->data, pstr->len+1, pstr->data);strcat_s(newNode->data, total, s.pstr->data);newNode->ref = 1;newNode->len = pstr->len + s.pstr->len;newNode->size = total; return String(newNode);}}
int main()
{String s1{ "baiU" };String s3("shiqianyu");s1 = s3 + s1;
}

s3+s1
调用了operator+完成
newNode =

return String(StrNode *p):pstr§
调用特殊的构造函数,创建一个将亡值对象,让该将亡值对象中的ptr将newNode指向的空间

最后:s1 = s3 + s1 ==》 s1->ptr = ptr;
调用移动赋值让s1对象中的ptr指向将亡值对象中的ptr;

+=运算符重载

String& operator +=(const String& s)
{if (pstr != NULL && s.pstr != NULL){if (pstr->ref > 1){int total = pstr->len + s.pstr->len;pstr->ref -= 1;char* tmp = pstr->data; //pstr = (StrNode*)malloc(sizeof(StrNode) + total * 2 + 1);strcpy(pstr->data, tmp);strcat(pstr->data, s.pstr->data);pstr->ref = 1;pstr->len = total;pstr->size = total * 2;}else{int total = pstr->len + s.pstr->len;if (pstr->size < total){pstr = (StrNode*)realloc(pstr, sizeof(StrNode) + total * 2 + 1);pstr->size = total * 2;}strcat(pstr->data, s.pstr->data);pstr->len = total;}}else if (this->pstr == NULL && s.pstr != NULL){pstr = s.pstr;pstr->ref += 1;}return *this;
}
int main()
{String s1{ "baiU" };String s3("shiqianyu");s1 += s3;
}
  1. 判断thsi指针所指对象的引用计数大于1,则开辟空间。
  2. 如果引用计数等于1,那么如果size小于两个字符串长度之和,就对其扩容,否则直接拼接
  3. 如果this为空,另外一个对象不为空,则直接赋值给this所指的对象,引数加一
  4. 不满足上述条件什么都不做,直接返回this。

【C++】模拟String,柔性数组,运算符重载,写实拷贝相关推荐

  1. 【C++】运算符重载2-深拷贝深赋值、前加加后加加的重载

    深拷贝.深赋值 我们首先通过下面这段代码来研究一下深拷贝和浅拷贝,区分一下什么时候需要我们自己来写拷贝构造函数和赋值运算符重载的函数. class Test {private:int m_a = 1; ...

  2. 运算符重载(加减运算符、前置加加(减减)后置加加(减减)运算符、赋值运算符、输入输出运算符、关系运算符、函数调用)

    编译器对于一个类会默认生成以几种函数: 1.默认构造函数(空形参,空函数体) 2.默认拷贝构造函数(浅拷贝,也叫值拷贝.字节拷贝) 3.析构函数(空形参,空函数体.析构函数要求形参列表必须是空的,所以 ...

  3. c++--运算符重载

    第14章 重载运算与类型转换 1 class Sales_data 2 { 3 /*重载<<和+运算符*/ 4 friend ostream& operator<<(o ...

  4. C++运算符重载典型习题---复数类 String类 分数类

    一.复数类(Complex class) complex.cpp: /***************************************************** copyright ( ...

  5. 关于string字符串大小比较以及运算符重载

                            关于string字符串大小比较以及运算符重载  今天在写项目的时候遇到两个时间字符串需要比较大小的情况,例:"20181023"和& ...

  6. string类的实现(构造函数,析构函数,运算符重载)

    String类的代码: class String { public:String(char* str=""){_str = new char[strlen(str) + 1];st ...

  7. C++实现:自定义数组类型实现相关运算符重载

    运算符重载说明 这次我们通过来自定义数组类型重载[].<<.>>.==.!=运算符来学习C++运算符重载相关知识. C++的运算符重载是C++相对于其他编程语言的一大特色. 在 ...

  8. 从零开始学C++之运算符重载(三):完善String类([]、 +、 += 运算符重载)、和运算符重载...

    在前面文章中使用过几次String类的例子,现在多重载几个运算符,更加完善一下,并且重载流类运算符. []运算符重载 +运算符重载 +=运算符重载 <<运算符重载 >>运算符重 ...

  9. 【C++的探索路8】运算符重载为友元以及可变长数组程序编写

    本篇文章内容首先对运算符重载为友元进行介绍,由于运算符重载这部分内容相对比较多,在结束友元的介绍后,通过一个综合性的小练习对前面的重载部分内容进行一个总结回顾. 运算符重载为友元 由前面关于友元的内容 ...

  10. 编写一个程序,用户使用for循环输入5个数字,所有这些数字将存储在一个数组中。之后,程序将添加这五个数字并显示结果。程序必须支持运算符重载的概念。

    Write a program in which users enter 5 numbers using for loop and all these numbers will store in an ...

最新文章

  1. 少走弯路:强烈推荐的TensorFlow快速入门资料(可下载)
  2. Laplace数值逆运算的讨论
  3. Redis基数统计——HyperLogLog小内存大用处
  4. Linux_ISCSI服务器
  5. java对象的类型转换_Java对象的类型转换和属性复制
  6. Python 第三方库之 docxtpl (处理word文档)
  7. Java Collections.emptyList() 方法的使用及注意事项
  8. 树的直径(51Nod-2602)
  9. php 长剑设计模式,PHP设计模式(创建型)
  10. 山东财经大学计算机体系结构考试题,2016年山东财经大学计算机科学与技术学院数据库系统原理与程序设计复试笔试仿真模拟题...
  11. Vue删除表格中的某一行数据
  12. 【原创】【android逆向系列】1:真机(小米note 3)root(从本人简书博客移入)
  13. 什么是网站被黑,如何处理网站被黑?
  14. matlab中P代码即P文件加密与逆向工程探讨
  15. Go2Shell 打开设置窗口
  16. 全局择优搜索、A*算法、宽度优先算法解决八数码问题
  17. 网站权重8 的网站优化方案
  18. elastic search7 java开发简单教程
  19. Day06-Python文件和数据格式化
  20. 奇数页 偶数页 页脚不一致怎么处理

热门文章

  1. Python滑块验证码之腾讯防水墙简单测试版
  2. 2017计算机四级网络工程师,2017计算机四级网络工程师真题练习
  3. 人寿保险的十大真相 保险不是什么时候都能买的
  4. 02_性能_内存调整_个人学习小结
  5. 开发者,熊二都会的Linux入门
  6. 仅15%的L2智能驾驶搭载DMS,「安全」背后的市场爆发在即
  7. Pygame简易版2048小游戏:超详细解说,看完还不会可以剁手了(附完整源码)
  8. CorelDRAW2023全新版功能及下载安装教程
  9. onclick事件诡异事件 一
  10. 搜狗AI走向产业改造:纵横捭阖术与录音笔的新声态