以后处理字符串可以直接用String类型 #include
编程能力强不强=处理字符串的能力强不强
C++中的string是一个标准库类型,表示可变长的字符序列,而由于是标准库类型,所以string定义在命名空间std中
ps:
1.得到字符串长度 str2.length()
2.底层是动态的 所以定义指针好
3.字符串哲学系都可能用完释放 所以要提供右值
4.常对象调不了普通方法,所以只读不写都定义为常方法
自写C++标准库中的String类型
class CMyString
{
public:
CMyString(const char*p = nullptr)
//本来是一个有参数的,无参数的 我们写一个带默认值的就可以无参数有参数结合在一起
//无参数的时候p = nullptr 有参数的时候就是用我传入的p
{
if (p != nullptr)
{
// 1.根据p指向得字符串得长度,开辟一块内存空间
mptr = new char[strlen(p) + 1];
// 2.把p指向得字符串拷贝到这块空间上来
strcpy(mptr, p);
}
else
{
//若p传进来是空的,则mptr开辟的空间也为空,所有自构造的时候就要保证:不管传进来的指针是否为空 都能构造出一个string底层空间不为空
mptr = new char[1];
*mptr = 0; // '\0'
}
}
~CMyString()
{
delete []mptr;
mptr = nullptr;
}
CMyString(const CMyString &str)
{
mptr = new char[strlen(str.mptr) + 1];
//最好不要写成 mptr = new char[str.length() + 1];因为接口之间不要有依赖性,不然length函数删了就要改好多
strcpy(mptr, str.mptr);
}
CMyString(CMyString &&str)
{
mptr = str.mptr;
str.mptr = nullptr;
}
// str1 = str2.operator=(str3);
CMyString& operator=(const CMyString &str)
{
if (this == &str)
return *this;
delete[]mptr;
mptr = new char[strlen(str.mptr) + 1];
strcpy(mptr, str.mptr);
return *this;
}
CMyString& operator=(CMyString &&str)
{
if (this == &str)
return *this;
delete[]mptr;
mptr = str.mptr;
str.mptr = nullptr;
return *this;
}
bool operator>(const CMyString &str)const
{
return strcmp(mptr, str.mptr) > 0;
}
bool operator<(const CMyString &str)const
{
return strcmp(mptr, str.mptr) < 0;
//strcmp是从两个字符串的第一个字母开始比较 看哪个在前面
}
bool operator==(const CMyString &str)const
{
return strcmp(mptr, str.mptr) == 0;
}
int length()const { return strlen(mptr); }
char& operator[](int index) { return mptr[index]; }
//要加& 返回地址,既能读又能写,不然的话只能看到那个值 不能改
//char是<4 由寄存器返回,那是个立即数,不可写只可读
const char* c_str()const { return mptr; }
//这个就是只能读 是个常方法
private:
char *mptr;
friend ostream& operator<<(ostream &out, const CMyString &str);
friend CMyString operator+(const CMyString &l, const CMyString &r);
};
CMyString operator+(const CMyString &l, const CMyString &r)
{
int length = l.length() + r.length();
char* p = new char[length + 1];
strcpy(p, l.mptr);
strcat(p, r.mptr);
String tmp(p);
delete []p;
return tmp;
/*
//不能直接返回构造的这个 因为从始至终没有析构过他 会造成内存泄露所以要写成上面那样 原因在于用指针p指向的内存构造了一个临时对象: CMyString(const char*p = nullptr) 但是这个函数结束了p一直没有被析构掉 所以不能这样做
注意之前用int类型的值构造临时对象返回就不会出现这样的烦恼
但是这样做的效率非常低!!!
return String(p);
//不能直接这样 因为1里面根本没有充足的空间去接受
strcat(1.mptr, r.mptr);
*/
}
// CMyString str1 = str2 + str3;
// CMyString str1; str1= str2 + str3;
//访问的是私有成员 所以要声明为友元函数
ostream& operator<<(ostream &out, const CMyString &str)
{
out << str.mptr;
return out;
}
istream& operator>>(istream &in, MyString &str)
{
delete[]str.mpstr;
str.mpstr = new char[1024];
in >> str.mpstr;
return in;
}
int main()
{
CMyString str1; // string()
CMyString str2 = "aaa"; // string(const char*p = nullptr)
CMyString str3 = "bbb";
CMyString str4 = str2 + str3;
CMyString str5 = str2 + "ccc";
CMyString str6 = "ddd" + str3;//同样要提供全局方法
str6 = str2 + "eee";
cout << str6 << endl;
if (str2 > str3)
{
cout << "str2 > str3" << endl;
}
int size1 = str2.length();
int size2 = str3.length();
for (int i = 0; i < str6.length(); ++i)
{
// char& operator[](int index)
// str6[i] = 'a';
cout << str6[i] << " ";
//str6[i]比getcharAt(i)要好,因为万一这个str里面存的是数组 每一个str[i]都存的是一个数组 就无法返回了
//以及要提供方括号的重载方法
}
cout << endl;
// string str = "elwiyuiyw7386529" char* => string
char *pbuf = new char[str6.length() + 1];//申请空间
strcpy(pbuf, str6.c_str()); // c_str() const char*
//c_str把对象管理的字符串返回char*的数据.用 strcpy进行赋值
cout << pbuf << endl;
*/
return 0;
}
写代码的方法:
1.先列逻辑 不要写了又改 要写思路
2.然后再根据思路来写
// 1.根据p指向得字符串得长度,开辟一块内存空间
mptr = new char[strlen(p) + 1];
// 2.把p指向得字符串拷贝到这块空间上来
strcpy(mptr, p);
上面实现的String类型出现了问题:
对于加法运算符重载函数:
CMyString operator+(const CMyString &l, const CMyString &r)
{
int length = l.length() + r.length();
char* p = new char[length + 1];
strcpy(p, l.mptr);
strcat(p, r.mptr);
String tmp(p);
delete []p;
return tmp;
}
这样做的效率非常低!!!
解决方法:
定义类对象接收 这样的话在函数结束的时候就可以调用写的析构函数
CMyString operator+(const CMyString &l, const CMyString &r)
{
int length = l.length() + r.length();
String tmp;
tmp.mptr= new char[length + 1];
strcpy(tmp.mptr, l.mptr);
strcat(tmp.mptr, r.mptr);
return tmp;
}
String实现:
1.一个for循环,从后往前同时遍历lhs.strDigit和rhs.strDigit
2.相应的位置进行相加,但是相加的时候需要考虑进位,如果带进位,需要多加一个1
3.加完的结果直接存储,但是如果超过10,只记录%10的余数,然后还要记录进位
4.两个字符串有可能同时加完,也有可能短的完了,长的还没完,把长的直接放入最终结果(还是要考虑进位的)
999999876
9879
class BigInt
{
public:
BigInt(const char* digit) :strDigit(digit) {}
private:
string strDigit;
friend string operator+(const BigInt &lhs, const BigInt &rhs);
friend string operator-(const BigInt &lhs, const BigInt &rhs);
};
string operator+(const BigInt &lhs, const BigInt &rhs)
{
/*
1.一个for循环,从后往前同时遍历lhs.strDigit和rhs.strDigit
2.相应的位置进行相加,但是相加的时候需要考虑进位,如果带进位,需要多加一个1
3.加完的结果直接存储,但是如果超过10,只记录%10的余数,然后还要记录进位
4.两个字符串有可能同时加完,也有可能短的完了,长的还没完,把长的直接放入最终结果(还是要考虑进位的)
999999876
9879
*/
string result;
int i = lhs.strDigit.length() - 1;
int j = rhs.strDigit.length() - 1;
bool flag = false;
for (; i >= 0 && j >= 0; --i, --j)
{
int sum = lhs.strDigit[i] - '0' + rhs.strDigit[j] - '0';
if (flag) // 如果低位有进位,处理进位情况
{
sum += 1;
flag = false;
}
if (sum >= 10) // 当前相应位相加超过10,记录正确的结果和记录进位
{
sum = sum % 10;
flag = true;
}
result.push_back(sum + '0'); // 把相应位相加的结果进行存储
}
for (; i >= 0; --i) // 表示lhs还有剩余
{
int sum = lhs.strDigit[i] - '0';
if (flag)
{
sum += 1;
flag = false;
}
if (sum >= 10)
{
sum = sum % 10;
flag = true;
}
result.push_back(sum + '0');
}
for (; j >= 0; --j) // 表示lhs还有剩余
{
int sum = rhs.strDigit[j] - '0';
if (flag)
{
sum += 1;
flag = false;
}
if (sum >= 10)
{
sum = sum % 10;
flag = true;
}
result.push_back(sum + '0');
}
if (flag)
{
result.push_back('1');
}
i = 0;
j = result.length() - 1;
while (i < j)
{
char ch = result[i];
result[i++] = result[j];
result[j--] = ch;
}
return result;
}
string operator-(const BigInt &lhs, const BigInt &rhs)
{
/*
先判断谁大谁小
低位开始相减
*/
string maxstr = lhs.strDigit;
string minstr = rhs.strDigit;
string result;
bool minor = false;
bool flag = false;
if (maxstr.length() < minstr.length())
{
maxstr = rhs.strDigit;
minstr = lhs.strDigit;
minor = true;
}
else
{
if (maxstr.length() == minstr.length())
{
if (maxstr < minstr)
{
maxstr = rhs.strDigit;
minstr = lhs.strDigit;
minor = true;
}
else if (maxstr == minstr)
{
return "0";
}
}
}
int i = maxstr.length() - 1;
int j = minstr.length() - 1;
for (; i >= 0 && j >= 0; --i, --j)
{
int ret = maxstr[i] - minstr[j]; // 3 - 8 -5
if (flag)
{
ret -= 1;
flag = false;
}
if (ret < 0)
{
ret += 10;
flag = true;
}
result.push_back(ret + '0');
}
for (; i >= 0; --i)
{
int ret = maxstr[i] - '0'; // 3 - 8 -5
if (flag)
{
ret -= 1;
flag = false;
}
if (ret < 0)
{
ret += 10;
flag = true;
}
result.push_back(ret + '0');
}
string ret;
i = result.length() - 1;
for (; i >= 0; --i)
{
if (result[i] != '0')
break;
}
for (; i >= 0; --i)
{
ret.push_back(result[i]);
}
if (minor)
{
ret = "-" + ret;
}
return ret;
}
int main()
{
BigInt int1("3985629783586723745692364"); // BigInt(const char*)
BigInt int2("72567537245823985678236");
//4058197320832547731370600
cout << int1 + int2 << endl;
cout << int1 - int2 << endl; // string operator-(xx, xx)
/*
99999327
99999326
00000001
*/
BigInt int3("99999327"); // BigInt(const char*)
BigInt int4("99999326");
cout << int3 - int4 << endl;
cout << int4 - int3 << endl;
return 0;
}
方法二实现:
// 2、编程(网易笔试题): 请实现以下类的方法,完成大数的加减法(可以使用STL容器)
// 大整数类型
class BigInt
{
public:
BigInt(string str) :strDigit(str) {}
private:
string strDigit; // 使用字符串存储大整数
friend ostream& operator<<(ostream &out, const BigInt &src);
//输出的参数就是out 所以不能写在类内 写在类内会变成this ostream &out 但是参数的第一个得是out
friend BigInt operator+(const BigInt &lhs, const BigInt &rhs);
//不写全局函数的话可能会有string+bigint这种现象发生 为了避免麻烦 写成全局
};
ostream& operator<<(ostream &out, const BigInt &src)
{
out << src.strDigit;
return out;
}
// 大数加法
BigInt operator+(const BigInt &lhs, const BigInt &rhs)
{
string result;
bool flag = false; // 记录进位 默认给的是不进位的参数
int lsize = lhs.strDigit.length();//保存左边的数的位数
int rsize = rhs.strDigit.length();
// 遍历lhs和rhs的字符串
int i = lsize - 1, j = rsize - 1;//从数字的最后一位相加开始向前移动
for (; i >= 0 && j >= 0; --i, --j)
{
int ret = (lhs.strDigit[i] - '0') + (rhs.strDigit[j] - '0');//数字转字符串
if (flag)//如果有进位的话
{
ret += 1;
flag = false;
}
if (ret >= 10)
{
ret %= 10;
flag = true; // 记录需要进位
}
result.push_back(ret + '0');//字符串的底层就是一维数组 所以可以一个一个的存储相加后的数字
//result.insert(result.begin(), ret + '0');
}
// i >=0 j >= 0 lhs 有一个位数算到小于零以后就会跳出来 然后开始判断那个没加完
if (i >= 0)//左右两个数据长短不一样 如果右边加完了
{
while (i >= 0)
{
int ret = (lhs.strDigit[i] - '0');
if (flag)
{
ret += 1;
flag = false;
}
if (ret >= 10)
{
ret %= 10;
flag = true;
}
result.push_back(ret + '0');
i--;
}
}
else if (j >= 0) // rhs 左边加完了
{
while (j >= 0)
{
int ret = (rhs.strDigit[j] - '0');
if (flag)
{
ret += 1;
flag = false;
}
if (ret >= 10)
{
ret %= 10;
flag = true;
}
result.push_back(ret + '0');
j--;
}
}
if (flag)
{
result.push_back('1');
}
reverse(result.begin(), result.end());//string result string底层是一维数组,同样也封装了迭代器,begin和end方法返回迭代器 reverse把头尾颠倒
return result;
}
// 大数减法
BigInt operator-(const BigInt &lhs, const BigInt &rhs)
{
/*
首先要区分大数和小数 > < 并且要考虑相减的结果是否要加负号
*/
string result;
bool flag = false;
bool minor = false;
string maxStr = lhs.strDigit;//所以先假设maxstr>minstr
string minStr = rhs.strDigit;//strDigit是bigint这个对象的成员 是string类型的
if (maxStr.length() < minStr.length())
{
maxStr = rhs.strDigit;//在这做的处理是永远把大的放在左边 然后加负号是后续的处理
minStr = lhs.strDigit;
minor = true;//如果假设错了 则需minor=true来加负号
}
else if(maxStr.length() == minStr.length())
{
if (maxStr < minStr)
{
maxStr = rhs.strDigit;//在这做的处理是永远把大的放在左边 然后加负号是后续的处理
minStr = lhs.strDigit;
minor = true;
}
else if (maxStr == minStr)//两个一样 则相减为0
{
return string("0"); // BigInt(string)
}
}
int lsize = maxStr.length();
int rsize = minStr.length();
// 遍历lhs和rhs的字符串
int i = lsize - 1, j = rsize - 1;//string底层是数组,有五个数字存到0~4 所以-1
for (; i >= 0 && j >= 0; --i, --j)
{
int ret = maxStr[i] - minStr[j];//对应位置相减这里直接ASCII码就可以相减
if (flag)//如果上一位的运算需要借位
{
ret -= 1;//给这一位要-1
flag = false;//借位符变为不用借位
}
if (ret < 0)
{
ret += 10;//<0借一位
flag = true; // 记录需要借位
}
result.push_back(ret + '0');//转为字符串再放进去 从尾部放
}
// i >=0 j >= 0 lhs
while (i >= 0)//因为在前面已经处理过让左边的数字始终大于右边的
{//所以这里要判断当右边的位数减完了的时候
int ret = (maxStr[i] - '0');
if (flag)
{
ret -= 1;
flag = false;
}
if (ret < 0)
{
ret += 10;
flag = true;
}
result.push_back(ret + '0');
i--;
}
while (result.back() == '0')//处理一下a-b=0003的情况 把前面存在数组尾部的0都尾部删除
{
result.pop_back();
}
if (minor)//加负号
{
result.push_back('-');
}
reverse(result.begin(), result.end());//首尾颠倒
return result;
}
int main()
{
BigInt int1("9785645649886874535428765");
BigInt int2("9785645649886874575648564");
BigInt int3("9785645649886874535428765");
//28937707643477817736572188660008
//28937707643477817736572188660008
cout << int1 + int2 << endl;
//28937688072186517962823117802478
// 28937688072186517962823117802478
//-28937688072186517962823117802478
cout << int1 - int2 << endl;
//deque deq;
//first 1024 2*i(1024)+1 / 1024 = 2 2*i(1024)+1 % 1024
//deq[10];
return 0;
}
注意加减运算对于是否要-'0’的区别:=
对于减法:
int ret = maxStr[i] - minStr[j];//对应位置相减这里直接ASCII码就可以相减
因为:
3—ASCII码52
1—ASCII码50
相减就是2
对于加法:
int ret = (lhs.strDigit[i] - '0') + (rhs.strDigit[j] - '0');
因为相加的话显然不是正确的 所以每个数字都要减去0的ASCII码