C++学习11-String类型 & 大数加减

String类型 & 大数加减

String类型

以后处理字符串可以直接用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<int> 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码

发布了82 篇原创文章 · 获赞 7 · 访问量 4186

猜你喜欢

转载自blog.csdn.net/sunshine612/article/details/105008241