温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++如何实现MyString

发布时间:2022-02-23 15:00:28 来源:亿速云 阅读:113 作者:小新 栏目:开发技术

这篇文章主要介绍了C++如何实现MyString,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

MyString的构造、析构、拷贝构造、赋值运算

class String
{
	char* str;
public:
	String(const char* p = NULL) :str(NULL)
	{
		if (p != NULL)
		{
			str = new char[strlen(p) + 1];//strlen()计算至'\0'截至的字符数
			strcpy(str, p);
		}
		else
		{
			str = new char[1]; //额外提供一个空间
			*str = '\0';
		}
	}
	~String()
	{
		if (str != NULL)
		{
			delete[] str;
		}
		str = NULL;
	}

	//ostream& operator<<(const String* const this, ostream &out)
	ostream& operator<<(ostream& out)const //重载插入操作符
	{
		if (str != NULL)
		{
			out << str;
		}
		return out;
	}

	String(const String& s):str(NULL)
	{
		//str = s.str; 浅拷贝 是同一个空间,会造成一个空间释放两次
		//深拷贝
		str = new char[strlen(s.str)+1];
		strcpy(str, s.str);
	}
	String& operator=(const String& s)
	{
		if(this != &s)
		{
			delete[]str;
			str = new char[strlen(s.str)+1]
			strcpy(str,s.str);
		}
		return *this;
	}
};

ostream& operator<<(ostream& out, const String& s)
{
	s << out;
	//s.operator<<(cout);
	//operator<<(&s1,cout);
	return out;
}
int main()
{
	String s1("123");
	s1 << cout;
	//s1.operator<<(cout);
	//operator<<(&s1,cout);

	
	cout << s1 << endl;
	//operator<<(cout, s1);
}

前面之所以对空指针构建对象提供一个空间的原因:使其在赋值重载中只有指向堆区一种情况进行处理

C++如何实现MyString

通过此方式进行等号运算符重载,然后调动拷贝构造对s2进行重写构造

C++如何实现MyString

输出流重写

class String
{
	char* str;
public:
	String(const char* p = NULL) :str(NULL)
	{
		if (p != NULL)
		{
			str = new char[strlen(p) + 1];
			strcpy(str, p);
		}
		else
		{
			str = new char[1]; //额外提供一个空间
			*str = '\0';
		}
	}
	~String()
	{
		if (str != NULL)
		{
			delete[] str;
		}
		str = NULL;
	}

	//ostream& operator<<(const String* const this, ostream &out)
	ostream& operator<<(ostream& out)const //重载插入操作符
	{
		if (str != NULL)
		{
			out << str;
		}
		return out;
	}
};
int main()
{
	String s1("123");
	s1 << cout;
	//s1.operator<<(cout);
	//operator<<(&s1,cout);
}

在这里通过改写前的代码 operator<<(&s1,cout); 不难看出,将cout初始化out,随后将this.str输出至out

ostream& operator<<(ostream& out)const
此处只能使用引用,因为cout在ostream类中进行转移,该类将拷贝构造函数定义为保护访问属性,无法使用cout初始化out,继而只能使用引用;同样若我们不想使用实参去初始化形参,可以将拷贝构造函数定义为私有或保护类型

若希望输出符合cout << s1 << endl;此种形式,需要再写一个全局函数

class String
{
	char* str;
public:
	String(const char* p = NULL) :str(NULL)
	{
		if (p != NULL)
		{
			str = new char[strlen(p) + 1];
			strcpy(str, p);
		}
		else
		{
			str = new char[1]; //额外提供一个空间
			*str = '\0';
		}
	}
	~String()
	{
		if (str != NULL)
		{
			delete[] str;
		}
		str = NULL;
	}

	//ostream& operator<<(const String* const this, ostream &out)
	ostream& operator<<(ostream& out)const //重载插入操作符
	{
		if (str != NULL)
		{
			out << str;
		}
		return out;
	}
};

ostream& operator<<(ostream& out, const String& s)
{
	s << out;
	//s.operator<<(cout);
	//operator<<(&s1,cout);
	return out;
}
int main()
{
	String s1("123");
	s1 << cout;
	//s1.operator<<(cout);
	//operator<<(&s1,cout);

	cout << s1 << endl;
	//operator<<(cout, s1);
}

通过此种形式进行翻转,继而达到符合 cout << s1 << endl; 的形式

MyString加号运算符重载

int main()
{
	String s1("123");
	String s2("456");

	String s3;
	s3 = s1 + s2;
	S3 = s1 + "789";
	s3 = "789" + s1;
}

分别写三个加号运算符重载,来对应上面的三个情况(类+类、类+字符串、字符串+类)

	String operator+(const String& s)const
	{
		char *p = new char(strlen(this->str) + strlen(s.str) + 1);
		strcpy(p, this->str);
		strcat(p, s.str);
		return String(p);
	}

第一个为成员函数,但是存在内存泄漏,需要进行下面的步骤

C++如何实现MyString

在私有成员变量中,创建一个新的构造函数,直接将p给到str,而没有创建新的空间;并且在加号运算符重载进行修改使其调用私有的构造函数

private:
	String(char*p,int)//两个参数与公有构造区分
	{
		str = p;
	}
public:
	String operator+(const String& s)const
	{
		char *p = new char(strlen(this->str) + strlen(s.str) + 1);
		strcpy(p, this->str);
		strcat(p, s.str);
		return String(p,1);
	}

这样就解决了原本内存泄漏的问题
接下来完成剩余两个等号运算符重载

String operator+(const char* s)const
{
	char* p = new char(strlen(this->str) + strlen(s) + 1);
	strcpy(p, this->str);
	strcat(p, s);
	return String(p, 1);
	//return *this + String(s)
	//上面的方式更方便,但是会构造两个临时对象
}

此处需要写在类外,并且需要类内添加友元函数

friend String operator+(const char* t, const String s);

String operator+(const char* t, const String s)
{
	char* p = new char(strlen(s.str) + strlen(t) + 1);
	strcpy(p, s.str);
	strcat(p, t);
	return String(p, 1);
	//return String(p) + s; 与上面同理,并且不需要友元函数
}

讨论一个衍生问题

class String
{
private:
	char* str;
public:
	String(const char* p = NULL) :str(NULL)
	{
		if (p != NULL)
		{
			str = new char[strlen(p) + 1];
			strcpy(str, p);
		}
		else
		{
			str = new char[1]; //额外提供一个空间
			*str = '\0';
		}
	}
	~String()
	{
		if (str != NULL)
		{
			delete[] str;
		}
		str = NULL;
	}
	String(const String& s)
	{
		//str = s.str; 浅拷贝 是同一个空间,会造成一个空间释放两次
		//深拷贝
		str = new char[strlen(s.str)];
		strcpy(str, s.str);
	}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			delete[]str;
			str = new char[strlen(s.str)];
			strcpy(str, s.str);
		}
		return *this;
	}
};
String fun()
{
	String s2("456");
	return s2;
}
int main()
{
	String s1;
	s1 = fun();

	return 0;
}

讨论此程序执行的过程总共创建了多少个对象:

C++如何实现MyString

主函数运行首先开辟main函数栈帧,创建s1对象,默认构造只有大小为一的空间存放“\0”;之后执行fun()函数,分配fun栈帧,然后创建s2对象,创建一个堆区,str指向堆区空间;并且将按值返回,需要构建一个临时对象(将亡值);

将亡值概念:表达式过程中所产生的不具有名字的一个实体,叫做将亡值;将亡值的生存期仅在表达式的调用过程中,表达式调用结束,将亡值就会结束

构建临时对象调用拷贝构造,fun函数结束,s2生存期结束,调动析构函数;首先释放s2调用资源,再归还s2空间;回到主函数,把将亡值赋值给s1调用赋值语句,接着调用将亡值的析构函数进行释放

这个过程中总共创建了三个对象,分别是s1、s2、将亡值对象

那么如果我们对fun以引用进行返回

String& fun()
{
	String s2("456");
	return s2;
}
int main()
{
	String s1;
	s1 = fun();
	
	return 0;
}

当以引用返回,就不会返回一个s2的备份,从引用底层来看会返回s2的地址;这样会从一个已死亡对象来获取数据,继而会得到随机值

C++如何实现MyString

感谢你能够认真阅读完这篇文章,希望小编分享的“C++如何实现MyString”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI