自实现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;
}