134-C++学习第八弹

重载[]

版本一

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;

完成不了,其他线程也不能进行

猜你喜欢

转载自blog.csdn.net/LINZEYU666/article/details/112683141