重载[]
版本一
char&operator[](const int index)
{
assert(index>=0&&index<strlen(str));
return str[index];
}
const char&operator[](const int index)const
{
assert(index>=0&&index<strlen(str));
return str[index];
}
const char*c_str()const
{
return str;
}
int main()
{
String s1("yhpinghello");
char x=s1[1];
return 0;
}
柔性数组
对于柔性数组,不计算在结构体的大小中,结构体的大小为8
结构体占8个字节,剩下的给data
重载[]版本二
class String
{
struct StrNode
{
int len; // 字符串的长度
int size; // 记录柔性数组的大小;sizeof(StrNode)+len
//int ref; // 引用计数
atomic<int> ref;
char data[];// 柔性数组 存字符串
};
private:
StrNode* str;
public:
String(const char *s = nullptr) :str(nullptr)//构造函数
{
if (s != nullptr) // s = "tulun"
{
int len = strlen(s);
str = (StrNode*)malloc(sizeof(StrNode) + 2 * (len + 1));
if (str == nullptr) exit(EXIT_FAILURE);//申请空间失败
str->len = len;
str->size = 2 * (len + 1);//空间的大小,不算结构体,只记录柔性数组大小
str->ref = 1; //有对象指向字符串
strcpy_s(str->data, len + 1, s);
}
}
~String()//析构函数
{
if (str != nullptr && --str->ref == 0)
{
free(str);
}
str = nullptr;
}
}
拷贝构造函数
String(const String &s):str(s.str)//拷贝构造函数
{
if (str != nullptr)
{
str->ref += 1;
}
}
int main()
{
String s1("yhpinghello");
String s2(s1);
String s3(s1);
String s4(s1);
return 0;
}
构造过程如下图所示
析构时,ref–;
如果s1已经被析构,不能拿去构建s2 s3 s4了
重载赋值函数
int main()
{
String s1("yhpinghello");
String s2(s1);
String s3("tulun");
String s4(s3);
s2 = s3;
return 0;
}
当执行s2=s3时
s2的str不指向yhpinghello,yhpinghello这个空间中的ref变为1,s2的str指向tulun,tulun的这个空间的ref变为3
如果又执行s1=s3
s1的str指向tulun,yhpinghello这个空间中的ref变为0,tulun的这个空间的ref变为4
String& operator=(const String& s)
{
if (this != &s)
{
if (this->str != nullptr && --this->str->ref == 0)
{
free(str);
}
str = s.str;
if (str != nullptr)
{
str->ref += 1;
}
}
return *this;
}
看下面这段代码
String fun()
{
String tmp("yhping");
return tmp; // return String("yhping");
}
int main()
{
String s1("tulun");
String s2(s1);
String s3 = fun();
String s4;
s4 = fun();
return 0;
}
String(String&& s)
{
str = s.str;
s.str = nullptr;
}
String& operator=(String&& s)
{
if (this != &s)
{
if (this->str != nullptr && --this->str->ref == 0)
{
free(str);
}
str = s.str;
s.str = nullptr;
}
return *this;
}
以return String(“yhping”);这种方式构建,系统优化了,return 构建对象直接就是s3本身
如果是构建tmp,首先构建tmp对象,然后把tmp赋值构建s3,移动构造
以上这两种情况下,如何重载+=?
写时拷贝
重载+=
如果tulun字符串太长,需重新申请
最好的方法如下
String& operator+=(const String& s)
{
// s1 += s2;
//NULL NULL;
//NULL not
//not NULL;
//not not;
if (this->str == nullptr || s.str == nullptr)
{
this->str = s.str;
if (this->str != nullptr)
{
this->str->ref += 1;
}
}
else if (this->str != nullptr && this->str->ref == 1)
{
int dist = this->str->size - this->str->len - 1;
if (dist >= s.str->len)
{
strcat_s(this->str->data,this->str->size, s.str->data);
}
else//剩余空间拷贝不进去
{
str=(StrNode*)realloc(sizeof(StrNode)+sp->len + s.str->len + 1);
strcat_s(this->str->data,this->str->size, s.str->data);
}
}
else
{
StrNode* sp = this->str;
this->str->ref -= 1;
this->str = nullptr;
int total = sp->len + s.str->len + 1;//总的字节数
str = (StrNode*)malloc(sizeof(StrNode) + 2 * total);//开辟空间
str->len = total - 1;//字符串长度
str->size = 2 * total;//空间大小个数
str->ref = 1;
strcpy_s(str->data,str->size, sp->data);
strcat_s(str->data, str->size, s.str->data);
}
return *this;
}
重载[]
int main()
{
String s1("yhpinghello");
String s2(s1);
String s3("tulun");
char x = s3[1];
x = s1[2];
s1[2] = 'W';
return 0;
}
const char& operator[](const int index) const//常方法,不改变
{
assert(str != nullptr && index >= 0 && index < str->len);
return str->data[index];
}
char& operator[](const int index)//普通方法
{
assert(str != nullptr && index >= 0 && index < str->len);
if (str->ref > 1)
{
str->ref -= 1;//断绝关系
StrNode* sp = str;
str = (StrNode*)malloc(sizeof(StrNode) + sp->size);
str->len = sp->len;
str->size = sp->size;
str->ref = 1;
strcpy_s(str->data, str->size, sp->data);
}
return str->data[index];
}
典型的写时拷贝
funA,构建sa,调用拷贝构造函数,不为空,ref为1,首先把1取出来,要加1之后再写进去,但是把这个1已经读出来了,要加1写进去的时候,被打断了,线程B进来,构建b对象,进入,不为空,ref取出来为1,加1后为2,准备后续的时候,前面读的1(脏数)加进去,最后为2,竞争关系,导致三个对象,ref=2,程序退出的时候,ref–,ref=0,导致最后程序失败
线程的争夺资源
如何改变?
ref不要定义整型量
++a执行,有很多行机器码
有可能被别的语句打断
如何解决
原子操作(不可分割)
atomic<int> ref;
完成不了,其他线程也不能进行