06-C++类和对象强化

打印 上一主题 下一主题

主题 1850|帖子 1850|积分 5550

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
一、实现 String 类

1.string 类先容

string 类是 c++ 里一个字符串操纵相关的类,简化了一些复杂的字符串操纵。


  • 在C语言中的一些字符串操纵
  1. int main() {
  2.     // 定义字符数组并初始化
  3.     char str[32] = "hello world";
  4.     char str2[32] = "";
  5.     // 字符串拷贝
  6.     strcpy(str2, str);
  7.     printf("str2 = %s\n", str2);
  8.    
  9.     // 字符串长度
  10.     int len = strlen(str2);
  11.     printf("str2长度为:%d\n", len);
  12.     // 比较字符串大小
  13.     char str3[32] = "hallo world";
  14.     int ret = strcmp(str2, str3);
  15.     if (ret > 0)
  16.         printf("%s 大于 %s\n", str2, str3);
  17.     else if (ret == 0)
  18.         printf("%s 等于 %s\n", str2, str3);
  19.     else
  20.         printf("%s 小于 %s\n", str2, str3);
  21.     return 0;
  22. }
复制代码


  • 运行效果
  1. str2 = hello world         
  2. str2长度为:11              
  3. hello world 大于 hallo world
复制代码


  • 通过 string 类实现上面的字符串操纵
  1. int main() {
  2.     // 定义字符数组并初始化
  3.     string str = "hello world";
  4.     string str2 = "";
  5.     // 字符串拷贝
  6.     str2 = str;
  7.     cout << "str2 = " << str2 << endl;
  8.     // 字符串长度
  9.     int len = str2.size();
  10.     cout << "str2长度为:" << len << endl;
  11.     // 比较字符串大小
  12.     string str3 = "hallo world";
  13.     if (str2 > str3)
  14.         cout << str2 << " 大于 " << str3 << endl;
  15.     else if (str2 == str3)
  16.         cout << str2 << " 等于 " << str3 << endl;
  17.     else
  18.         cout << str2 << " 小于 " << str3 << endl;
  19.     return 0;
  20. }
复制代码


  • 运行效果
  1. str2 = hello world         
  2. str2长度为:11              
  3. hello world 大于 hallo world
复制代码


  • 说明:上面分别是C语言实现的字符串的根本操纵,以及通过c++ string 类实现的字符串的根本操纵,可以看出,string 类用起来会更加方便。
2.实现本身的 MyString 类

这里实现一些 String 类中比较简朴常用的功能。
2.1 MyString 类的成员

先设计好 MyString 类有哪些成员变量和成员函数。


  • 类的界说
  1. // 定义一个 MyString 类
  2. class MyString
  3. {
  4.     friend istream& operator>>(istream &in, MyString &obj);
  5.     friend ostream& operator<<(ostream &out, MyString obj);
  6. private:
  7.     // 字符串的长度
  8.     int size;
  9.     // z字符串首元素地址
  10.     char *str;
  11. public:
  12.     // 构造函数
  13.     MyString();
  14.     MyString(char *str);
  15.     MyString(const MyString &obj);
  16.     // 析构函数
  17.     ~MyString();
  18.     // 获取字符串的长度
  19.     int Size();
  20.     // 重载运算符
  21.     char& operator[](int index);
  22.     MyString& operator=(MyString &obj);
  23.     MyString& operator=(char *str);
  24.     bool operator>(MyString &obj);
  25.     bool operator==(MyString &obj);
  26.     bool operator<(MyString &obj);
  27.     bool operator>(char *str);
  28. };
复制代码
2.2实现 MyString 的拷贝析构

MyString 类的无参构造、有参构造、拷贝构造、析构函数的代码实现。


  • 构造析构实现
  1. MyString::MyString() {
  2.     size = 0;
  3.     str = NULL;
  4. }
  5. MyString::MyString(char *str) {
  6.     size = strlen(str);
  7.     // 多加一个放 \0
  8.     this->str = new char[size + 1];
  9.     // strcpy 会拷贝 \0
  10.     strcpy(this->str, str);
  11. }
  12. MyString::MyString(const MyString &obj) {
  13.     size = obj.size;
  14.     this->str = new char[size + 1];
  15.     strcpy(str, obj.str);
  16. }
  17. MyString::~MyString() {
  18.     if (str != NULL)
  19.     {
  20.         delete [] str;
  21.         str = NULL;
  22.     }
  23. }
复制代码
2.3得到字符串的长度

即得到字符串对象中第一个字符到 \0 之间的字符的个数。


  • 字符串长度
  1. int MyString::Size() {
  2.     return size;
  3. }
复制代码
2.4输出字符串

如果我们直接 cout 输出字符串对象,因为对象中的成员除了字符串首元素地址,还有字符串的长度,因此不能直接输出,须要重载 << 运算符,又因为运算符的左值不是自界说对象,因此要通过全局函数重载 << 运算符,同时别忘了将函数添加友元。


  • 输出字符串:
  1. // 打印字符串,重载 << 运算符
  2. ostream& operator<<(ostream &out, MyString obj)
  3. {
  4.     out << obj.str;
  5.     return out;
  6. }
复制代码
2.5输入字符串

键盘获取输入,为对象中成员变量赋值,但是须要实现cin >> 字符串对象,须要重载 >> 运算符。其左值也是一个非自界说对象,因此通过全局函数实现,将全局函数设置为友元。


  • 输入字符串
  1. // 输入字符串,重载 >> 运算符
  2. istream& operator>>(istream &in, MyString &obj)
  3. {
  4.     // 如果字符串不为空,先指向空
  5.     if (obj.str != NULL)
  6.     {
  7.         delete [] obj.str;
  8.         obj.str = NULL;
  9.     }
  10.     // 定义一个临时变量存放键盘输入的值
  11.     char temp[1024] = "";
  12.     in >> temp;
  13.     obj.size = strlen(temp);
  14.     obj.str = new char[obj.size + 1];
  15.     strcpy(obj.str, temp);
  16.     return in;
  17. }
复制代码


  • 说明:

    • 这里的输入不是追加,因为不知道用户具体会输入多少字符,不确定以前的空间是否足够,因此先判断以前的空间是否为 NULL ,开释之前的空间,申请新空间存放字符串;
    • 因为不确定用户输入多少字符串,不知道该申请多大的空间,于是通过一个暂时变量保存字符串,再测字符串长度,根据现实长度申请堆区空间;
    • 这里是要对对象的字符串指针变量写入值,要改变其内容,因此利用引用传递。

2.6操纵单个字符

操纵单个字符,就是通过下标索引,读取或修改字符串中的某个字符,须要重载 [] 运算符。左值为自界说对象,通过类的成员函数实现运算符重载。


  • 重载 []
  1. char &MyString::operator[](int index) {
  2.     // 先判断是否为空
  3.     if (NULL == str)
  4.     {
  5.         cout << "字符串为空,无法操作" << endl;
  6.         exit(-1);
  7.     }
  8.     // 判断索引是否越界
  9.     if (index >=0 && index <size)
  10.     {
  11.         return str[index];
  12.     }
  13.     else
  14.     {
  15.         cout << index << "越界" << endl;
  16.         exit(-1);
  17.     }
  18. }
复制代码


  • 说明:exit 是直接退出当前历程。
2.7对象给对象赋值

一个对象给另外一个对象赋值,须要重载 = 运算符,同时考虑两种情况,一种是 对象1 = 对象2,另一种是对象 = 字符串。


  • 对象给对象赋值
  1. // 对象赋值给对象
  2. MyString& MyString::operator=(MyString &obj) {
  3.     // 先判断左值是否为空,非空要先释放
  4.     if (str != NULL)
  5.     {
  6.         delete [] str;
  7.         str = NULL;
  8.     }
  9.     // 将右变对象的值拷贝给左边对象,深拷贝
  10.     size = obj.size;
  11.     str = new char[size + 1];
  12.     strcpy(str, obj.str);
  13.     return *this;
  14. }
  15. // 将字符串赋值给对象
  16. MyString& MyString::operator=(char *str) {
  17.     // 先判断左值是否为空,非空要先释放
  18.     if (this->str != NULL)
  19.     {
  20.         delete [] this->str;
  21.         this->str = NULL;
  22.     }
  23.     // 将右变对象的值拷贝给左边对象,深拷贝
  24.     size = strlen(str);
  25.     this->str = new char[size + 1];
  26.     strcpy(this->str, str);
  27.     return *this;
  28. }
复制代码


  • 说明:

    • 上面的第一个函数中,如果返回值为引用,那么参数也必须为引用,且第二个参数的返回值也必须为引用,目的是为了保证链式操纵中类型匹配;
    • 假设一种情况 str3 = str2 = str1 = "hello world":会先执行str1 = "hello world",调用上面的第二个函数,然后将赋值后的 str1 的引用返回。此时再执行str2 = str1,将上一次执行的效果作为参数传给第一个函数的 MyString &obj,参数为引用,类型匹配,如果第二个函数返回的是值不是引用,那么这里的链式操纵就完成不了。完成了将 str1 的值赋给 str2 后,会继承返回 str2 的引用,再次调用第一个函数,将上一次执行的效果 str2 的引用传给MyString &obj,如果第一个函数的返回值不是引用,是值的话,这里传参对值取别名会报错,因此返回值和参数的类型必须匹配。

2.8比较字符串对象大小

比较字符串大小,即重载 > < == 运算符。


  • 比较字符串大小
  1. // 重载 > 运算符
  2. bool MyString::operator>(MyString &obj) {
  3.     if ((str == NULL) || (obj.str == NULL))
  4.     {
  5.         cout << "存在空字符串,无法比较" << endl;
  6.         exit(-1);
  7.     }
  8.     if (strcmp(str, obj.str) > 0)
  9.         return true;
  10.     return false;
  11. }
  12. // 重载 == 运算符
  13. bool MyString::operator==(MyString &obj) {
  14.     if ((str == NULL) || (obj.str == NULL))
  15.     {
  16.         cout << "存在空字符串,无法比较" << endl;
  17.         exit(-1);
  18.     }
  19.     if (strcmp(str, obj.str) == 0)
  20.         return true;
  21.     return false;
  22. }
  23. // 重载 < 运算符
  24. bool MyString::operator<(MyString &obj) {
  25.     if ((str == NULL) || (obj.str == NULL))
  26.     {
  27.         cout << "存在空字符串,无法比较" << endl;
  28.         exit(-1);
  29.     }
  30.     if (strcmp(str, obj.str) < 0)
  31.         return true;
  32.     return false;
  33. }
复制代码
除了比较两个字符串对象的大小,也可能比较一个字符串对象和一个字符串的大小,这里只以重载 > 举例。


  • 比较字符串对象与字符串的大小
  1. bool MyString::operator>(char *str) {
  2.     if ((this->str == NULL) || (str == NULL))
  3.     {
  4.         cout << "存在空字符串,无法比较" << endl;
  5.         exit(-1);
  6.     }
  7.     if (strcmp(this->str, str) > 0)
  8.         return true;
  9.     return false;
  10. }
复制代码


  • 说明:比较两个字符串对象大小,以及比较字符串对象和字符串大小时,一定要先判断字符串是否指向 NULL,否则可能访问非法内存,导致段错误。
二、MyString 类完备代码



  • 代码演示
  1. #include <iostream>#include <string.h>using namespace std;// 界说一个 MyString 类class MyString{    friend istream& operator>>(istream &in, MyString &obj);    friend ostream& operator<<(ostream &out, MyString obj);private:    // 字符串的长度    int size;    // z字符串首元素地址    char *str;public:    // 构造函数    MyString();    MyString(char *str);    MyString(const MyString &obj);    // 析构函数    ~MyString();    // 获取字符串的长度    int Size();    char& operator[](int index);    MyString& operator=(MyString &obj);    MyString& operator=(char *str);    bool operator>(MyString &obj);    bool operator==(MyString &obj);    bool operator<(MyString &obj);    bool operator>(char *str);};MyString::MyString() {
  2.     size = 0;
  3.     str = NULL;
  4. }
  5. MyString::MyString(char *str) {
  6.     size = strlen(str);
  7.     // 多加一个放 \0
  8.     this->str = new char[size + 1];
  9.     // strcpy 会拷贝 \0
  10.     strcpy(this->str, str);
  11. }
  12. MyString::MyString(const MyString &obj) {
  13.     size = obj.size;
  14.     this->str = new char[size + 1];
  15.     strcpy(str, obj.str);
  16. }
  17. MyString::~MyString() {
  18.     if (str != NULL)
  19.     {
  20.         delete [] str;
  21.         str = NULL;
  22.     }
  23. }
  24. int MyString::Size() {
  25.     return size;
  26. }
  27. // 打印字符串,重载 << 运算符
  28. ostream& operator<<(ostream &out, MyString obj)
  29. {
  30.     out << obj.str;
  31.     return out;
  32. }
  33. // 输入字符串,重载 >> 运算符
  34. istream& operator>>(istream &in, MyString &obj)
  35. {
  36.     // 如果字符串不为空,先指向空
  37.     if (obj.str != NULL)
  38.     {
  39.         delete [] obj.str;
  40.         obj.str = NULL;
  41.     }
  42.     // 定义一个临时变量存放键盘输入的值
  43.     char temp[1024] = "";
  44.     in >> temp;
  45.     obj.size = strlen(temp);
  46.     obj.str = new char[obj.size + 1];
  47.     strcpy(obj.str, temp);
  48.     return in;
  49. }
  50. char &MyString::operator[](int index) {
  51.     // 先判断是否为空
  52.     if (NULL == str)
  53.     {
  54.         cout << "字符串为空,无法操作" << endl;
  55.         exit(-1);
  56.     }
  57.     // 判断索引是否越界
  58.     if (index >=0 && index <size)
  59.     {
  60.         return str[index];
  61.     }
  62.     else
  63.     {
  64.         cout << index << "越界" << endl;
  65.         exit(-1);
  66.     }
  67. }
  68. MyString &MyString::operator=(MyString &obj) {    // 先判断左值是否为空,非空要先开释    if (str != NULL)    {        delete [] str;        str = NULL;    }    // 将右变对象的值拷贝给左边对象,深拷贝    size = obj.size;    str = new char[size + 1];    strcpy(str, obj.str);    return *this;}MyString& MyString::operator=(char *str) {    // 先判断左值是否为空,非空要先开释    if (this->str != NULL)    {        delete [] this->str;        this->str = NULL;    }    // 将右变对象的值拷贝给左边对象,深拷贝    size = strlen(str);    this->str = new char[size + 1];    strcpy(this->str, str);    return *this;}bool MyString::operator>(MyString &obj) {    if ((str == NULL) || (obj.str == NULL))    {        cout << "存在空字符串,无法比较" << endl;        exit(-1);    }    if (strcmp(str, obj.str) > 0)        return true;    return false;}bool MyString::operator==(MyString &obj) {    if ((str == NULL) || (obj.str == NULL))    {        cout << "存在空字符串,无法比较" << endl;        exit(-1);    }    if (strcmp(str, obj.str) == 0)        return true;    return false;}bool MyString::operator<(MyString &obj) {    if ((str == NULL) || (obj.str == NULL))    {        cout << "存在空字符串,无法比较" << endl;        exit(-1);    }    if (strcmp(str, obj.str) < 0)        return true;    return false;}bool MyString::operator>(char *str) {
  69.     if ((this->str == NULL) || (str == NULL))
  70.     {
  71.         cout << "存在空字符串,无法比较" << endl;
  72.         exit(-1);
  73.     }
  74.     if (strcmp(this->str, str) > 0)
  75.         return true;
  76.     return false;
  77. }
  78. int main(){    cout << "------------------- 重载<< ------------------" << endl;    MyString str1 = "hello world!";    cout << str1 << endl;    // 得到字符串的长度    cout << "size = " << str1.Size() << endl;    cout << "------------------- 重载>> ------------------" << endl;    MyString str2;    cout << "请输入一个字符串(不带空格):";    cin >> str2;    cout << str2 << endl;    cout << "------------------- 重载[] ------------------" << endl;    MyString str3 = "hello";    cout << str3[4] << endl;    str3[1] = 'E';    cout << str3 << endl;    cout << "------------------- 重载= ------------------" << endl;    MyString str4;    MyString str5;    str5 = str4 = "hello human";    cout << str4 << endl;    cout << str5 << endl;    cout << "------------------- 重载比较运算符 ------------------" << endl;    MyString str6, str7;    cout << "请输入两个字符串:";    cin >> str6 >> str7;    if (str6 > str7)        cout << str6 << "大于" << str7 << endl;    else if (str6 == str7)        cout << str6 << "即是" << str7 << endl;    else        cout << str6 << "小于" << str7 << endl;}
复制代码


  • 运行效果
  1. ------------------- 重载<< ------------------
  2. hello world!
  3. size = 12
  4. ------------------- 重载>> ------------------
  5. 请输入一个字符串(不带空格):helloworld
  6. helloworld
  7. ------------------- 重载[] ------------------        
  8. o
  9. hEllo
  10. ------------------- 重载= ------------------
  11. hello human
  12. hello human
  13. ------------------- 重载比较运算符 ------------------
  14. 请输入两个字符串:hallo hello
  15. hallo小于hello
复制代码
三、其它知识点补充

1.不发起重载 && 和 ||

不要重载 && 和 || ,因为用户无法实现 && 和 || 的短路特性。


  • 代码演示
  1. class Complex {
  2. public:
  3.     int flag;
  4.    
  5.     // 构造函数
  6.     Complex(int flag) {
  7.         this->flag = flag;
  8.     }
  9.     // 重载 += 运算符
  10.     Complex &operator+=(Complex &complex) {
  11.         this->flag = this->flag + complex.flag;
  12.         return *this;
  13.     }
  14.     // 重载 && 运算符
  15.     bool operator&&(Complex &complex) {
  16.         return this->flag && complex.flag;
  17.     }
  18. };
  19. int main() {
  20.     Complex complex1(0); //flag 0
  21.     Complex complex2(1); //flag 1
  22.     if (complex1 && (complex1 += complex2)) {
  23.         cout << "真!
  24. " << endl;
  25.     } else {
  26.         cout << "假!" << endl;
  27.     }
  28.     return 0;
  29. }
复制代码


  • 运行效果
  1. 真!
复制代码


  • 说明

    • 对象complex1的 flag 变量值为0,按原理来说,执行的效果应该为 "“假!” ,但这里的效果却为 “真!”;
    • 因为在执行if (complex1 && (complex1 += complex2))的时候,(complex1 += complex2)会先触发operator+=(Complex &complex)函数的调用,从而将运算效果 1 赋值给 complex1 的 flag 导致最终的条件为 (1 && 1),因此效果为 “真!”;
    • 所以,我们重载 && 运算符的时候 没法实现其短路特性。

其它不能重载的运算符:. :: .* ?: sizeof。
2.智能指针

智能指针可以帮助我们省略一部分内存操纵,通过在外包裹一层外壳,间接主动实现堆区空间的开释等操纵。


  • 代码演示
  1. class Data {
  2. private:
  3.     int num;
  4. public:
  5.     Data() {
  6.         cout << "Data的无参构造" << endl;
  7.     }
  8.     Data(int num) {
  9.         this->num = num;
  10.         cout << "Data的有参构造 num=" << num << endl;
  11.     }
  12.     void showData() {
  13.         cout << "num=" << num << endl;
  14.     }
  15.     ~Data() {
  16.         cout << "Data的析构函数" << endl;
  17.     }
  18. };
  19. class SmartPoint {
  20. private:
  21.     Data *sp;
  22. public:
  23.     SmartPoint() : sp(NULL) {}
  24.     SmartPoint(Data *sp) {
  25.         this->sp = sp;
  26.     }
  27.     SmartPoint(const SmartPoint &ob) {
  28.         this->sp = new Data;
  29.         *sp = *ob.sp;
  30.     }
  31.     ~SmartPoint() {
  32.         delete sp;
  33.     }
  34.     Data *operator->() {
  35.         return sp;
  36.     }
  37.     Data &operator*() {
  38.         return *sp;
  39.     }
  40. };
  41. int main() {
  42.     SmartPoint bp(new Data(100));
  43.     bp->showData();
  44.     (*bp).showData();
  45.     return 0;
  46. }
复制代码


  • 运行效果
  1. Data的有参构造 num=100
  2. num=100               
  3. num=100               
  4. Data的析构函数
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王海鱼

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表