自实现string类的功能

自实现string类的功能

 

主函数

#include "stdafx.h"
#include <iostream>
using namespace std;

#include "String.h"


int _tmain(int argc, _TCHAR* argv[])
{
	String s;
	cout << s.c_str() << endl;

	String s1("china");
	cout << s1.c_str() << endl;


	String s2 = "china"; //等价于 String sss("china");
	cout << s2.c_str() << endl;


	String s3(s2);//拷贝行为 s2进行深拷贝得到s3 用一个对象产生另一个对象
	cout << s3.c_str() << endl;

	String s4 = s2;  
	//相当于深拷贝 String s4(s2); 本质也是拷贝构造 
	//用已有的一个对象 完成一个对象从无到有的创建
	cout << s4.c_str() << endl;


	String s5; //先创建了一个空串
	s5 = s2; //等价于 s5.operator=(s2); 相当于函数的调用
	cout << s5.c_str() << endl;


	String s6;
	s6 = s5 = s2;  //s6.operator=(s5.operator=(s2));
	cout << s6.c_str() << endl;


	String x = "abc";
	String y = "def";
	String z = x + y;
	cout << z.c_str() << endl;

	if (x == y)
		cout << "x=y" << endl;
	else if (x > y)
		cout << "x>y" << endl;
	else
		cout << "x<y" << endl;

	x[1] = '@';
	cout << x.c_str() << endl;
	cout << x.at(1) << endl;

	cout << "======================================" << endl;

	String a = "aa";
	String b = "bb";
	String c = "cc";

	(a = b) = c;

	//赋值运算符重载返回类型为 & 输出的结果:a=cc b=bb c=cc
	//赋值运算符重载返回类型为 对象 输出的结果:a=bb b=bb c=cc
	//原因查看后面注释或者 返回栈上的引用或对象 的文章

	cout << a.c_str() << endl;
	cout << b.c_str() << endl;
	cout << c.c_str() << endl;

	return 0;
}

String.h(大写的String,非系统string)

#pragma once
#include <iostream>
using namespace std;

class String
{
public:
	//String();
	String(const char* str=NULL);//只读字符串类型为const char*类型 不能修改
	String(const String & another);  //拷贝构造器
	~String(); //析构器
	String& operator=(const String &another);//赋值运算符的重载 赋值返回=左边的引用
	String operator+(const String &another); //求和产生的是新对象 返回的是新对象的值
	
	bool operator>(const String &another);
	bool operator<(const String &another);
	bool operator==(const String &another);
	char& operator[](int idx);
	char at(int idx);
	
	const char* c_str();//返回数据成员_str

private:
	char* _str; //String类型中存在指针
};

 

String.cpp(大写的String,非系统string)

#include "String.h"
#include <string.h>

//构造器
String::String(const char* str)
{
	if (str == NULL) //判断是否是默认的空串
	{
		_str = new char[1]; 
		//定义成[]是为了在析构器中delete可以和[len+1]一起delete

		*_str = '\0'; //生成一个空串
	}
	else
	{
		int len = strlen(str);

		_str = new char[len + 1]; 
		//注意len的长度是没有\0的长度 所以申请空间时要+1

		strcpy(_str, str);
	}
}


#if 0
----拷贝构造器:
1.格式  A(const A &another)

2.若不提供 系统会提供默认 一旦自定义 系统不再提供默认

3.系统提供的默认 是一种等位拷贝 也就是浅拷贝

4.浅拷贝会导致 内存的重析构 doublefree 
有些情况下(含有堆空间的时候) 要自实现拷贝构造
#endif

String::String(const String & another)
{
	//_str = another._str;等位拷贝 
	//此处 another可以访问private的_str 是因为同类对象间无隐私

	//等位拷贝在处理对象中含有在堆上申请的空间时 
	//俩个对象会指向同一个区域 在析构时会析构俩次

	int len = strlen(another._str);
	_str = new char[len + 1];
	strcpy(_str, another._str);
}


//析构器
String::~String()
{
	delete[]_str;
}


#if 0
----赋值运算符重载:
用一个己有对象,给另外一个己有对象赋值。
两个对象均己创建结束后,发生的赋值行为

1.编译器提供默认 也是一种等位赋值(本质也是等位拷贝) 
一旦自定义系统不再提供默认

2.默认赋值运算符重载也是一种等位赋值 浅赋值

3.浅赋值会导致两三问题
(1)自身内存泄漏
(2)内存发生重析构
(3)自赋值出错


----为什么赋值运算符(=)返回的类型为引用(&)而不是对象:

因为赋值这个行为,相当于=左边的对象调用重载函数将右值赋给左值,
并返回左值的引用。之所以可以在栈上 返回引用 是因为调用过程中,
=左边的对象并没有消失,返回引用不会出错。而且,如果要完成
(a=b)=c 这种行为,只有返回引用才能做到。因为首先b对a进行赋值,
也就是(a=b),这个式子完成后返回的是a的引用,再将c的值赋值给a的
引用,从而可以改变a的值使a=c。如果(a=b)返回类型为对象,存储它的
只是一个临时变量,也就是将c的值赋给这个临时变量,并不会改变原有a
的值,临时变量也会很快的消失。相当于你只改变了它的替身的值,并没
有改变它的真身。所以返回类型要为引用(&).
#endif


String& String::operator=(const String &another)
{
	if (this == &another) //首先判断是否是自赋值
		return *this;
	delete[]this->_str; 
	//先把自己释放掉 也就是开始创建的空串 不然的话 
	//以后指向关系发生改变就没机会释放 造成内存泄漏

	int len = strlen(another._str);
	this->_str = new char[len + 1];
	strcpy(this->_str, another._str);
	return *this;
}



#if 0
----加号运算符重载

----为什么加号运算符(+)重载返回类型为一个对象而不是引用(&):

因为加法是产生一个新数据,如x+y 它不会改变x和y的值,可以理解为
x调用加号重载函数,使x的值和y的值加到一起,但返回的对象不是x
也不是y。而是一个新对象,这个新对象是在栈上临时产生的,它不是这
个函数的调用者,它在函数结束后就会消失,如果此时再返回它的引用,
也不会得到它的内容,也就是它的真身已经不存在了。这时就必须要返回
它的一个替身,这个替身会在栈上新开辟一段空间存放内容,本质上是在
重载函数结束后,还能维持一段时间的临时变量。也就是现在x+y这个式子
的值。直到z来接受这个临时变量,也就是z=x+y,注意z一定是在等号左边
的,这时这个替身,也就是临时变量x+y的值,很快就会消失不见了,但已
经完成了所要实现的功能,所以加号重载函数要返回一个对象。

#endif 


String String::operator+(const String &another)
{
	String tmp; //新生成一个对象
	delete[] tmp._str;   //把开始的""清除掉
	int len = strlen(this->_str);
	len += strlen(another._str); 
	tmp._str = new char[len + 1];

	memset(tmp._str, 0, len + 1);  
	//必须进行清0操作 因为新申请空间内容未知 strcat后会出现未知内容

	strcat(tmp._str, this->_str);
	strcat(tmp._str, another._str);
	
	//strcpy(tmp._str, strcat(this->_str, another._str));
	//不能用这个方法 因为this中的_str和another中的_str的
	//空间大小都是根据自身长度分配的
	//没有多余的空间来strcat 而tmp的_str
	//有足够的空间大小来strcpy或strcat

	return tmp;
}


bool String::operator>(const String &another)
{
	if (strcmp(this->_str, another._str) > 0)
		return true;
	else
		return false;
}

bool String::operator<(const String &another)
{
	if (strcmp(this->_str, another._str) < 0)
		return true;
	else
		return false;
}

bool String::operator==(const String &another)
{
	if (strcmp(this->_str, another._str) == 0)
		return true;
	else
		return false;
}


//之所以返回值为char& 类型是因为可以根据引用类型对原值进行改变 
//如果为char类型则不能对原值进行更改
char& String::operator[](int idx) 
{
	return this->_str[idx]; //x[idx]; x.operator[](int idx)
}

char String::at(int idx) //at函数不能更改内容 所以返回为char
{
	return this->_str[idx];
}


char* String::c_str()
{
	return _str;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值