王海鱼 发表于 2025-1-7 02:58:19

06-C++类和对象强化

一、实现 String 类

1.string 类先容

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


[*]在C语言中的一些字符串操纵
int main() {
    // 定义字符数组并初始化
    char str = "hello world";
    char str2 = "";

    // 字符串拷贝
    strcpy(str2, str);
    printf("str2 = %s\n", str2);
   
    // 字符串长度
    int len = strlen(str2);
    printf("str2长度为:%d\n", len);

    // 比较字符串大小
    char str3 = "hallo world";
    int ret = strcmp(str2, str3);
    if (ret > 0)
      printf("%s 大于 %s\n", str2, str3);
    else if (ret == 0)
      printf("%s 等于 %s\n", str2, str3);
    else
      printf("%s 小于 %s\n", str2, str3);
    return 0;
}


[*]运行效果
str2 = hello world         
str2长度为:11            
hello world 大于 hallo world


[*]通过 string 类实现上面的字符串操纵
int main() {
    // 定义字符数组并初始化
    string str = "hello world";
    string str2 = "";

    // 字符串拷贝
    str2 = str;
    cout << "str2 = " << str2 << endl;

    // 字符串长度
    int len = str2.size();
    cout << "str2长度为:" << len << endl;

    // 比较字符串大小
    string str3 = "hallo world";
    if (str2 > str3)
      cout << str2 << " 大于 " << str3 << endl;
    else if (str2 == str3)
      cout << str2 << " 等于 " << str3 << endl;
    else
      cout << str2 << " 小于 " << str3 << endl;
    return 0;
}


[*]运行效果
str2 = hello world         
str2长度为:11            
hello world 大于 hallo world


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

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

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


[*]类的界说
// 定义一个 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);
};
2.2实现 MyString 的拷贝析构

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


[*]构造析构实现
MyString::MyString() {
    size = 0;
    str = NULL;
}

MyString::MyString(char *str) {
    size = strlen(str);
    // 多加一个放 \0
    this->str = new char;
    // strcpy 会拷贝 \0
    strcpy(this->str, str);
}

MyString::MyString(const MyString &obj) {
    size = obj.size;
    this->str = new char;
    strcpy(str, obj.str);
}

MyString::~MyString() {
    if (str != NULL)
    {
      delete [] str;
      str = NULL;
    }
}
2.3得到字符串的长度

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


[*]字符串长度
int MyString::Size() {
    return size;
}
2.4输出字符串

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


[*]输出字符串:
// 打印字符串,重载 << 运算符
ostream& operator<<(ostream &out, MyString obj)
{
    out << obj.str;
    return out;
}
2.5输入字符串

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


[*]输入字符串
// 输入字符串,重载 >> 运算符
istream& operator>>(istream &in, MyString &obj)
{
    // 如果字符串不为空,先指向空
    if (obj.str != NULL)
    {
      delete [] obj.str;
      obj.str = NULL;
    }

    // 定义一个临时变量存放键盘输入的值
    char temp = "";
    in >> temp;
    obj.size = strlen(temp);
    obj.str = new char;
    strcpy(obj.str, temp);
    return in;
}


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

2.6操纵单个字符

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


[*]重载 []
char &MyString::operator[](int index) {
    // 先判断是否为空
    if (NULL == str)
    {
      cout << "字符串为空,无法操作" << endl;
      exit(-1);
    }

    // 判断索引是否越界
    if (index >=0 && index <size)
    {
      return str;
    }
    else
    {
      cout << index << "越界" << endl;
      exit(-1);
    }
}


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

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


[*]对象给对象赋值
// 对象赋值给对象
MyString& MyString::operator=(MyString &obj) {
    // 先判断左值是否为空,非空要先释放
    if (str != NULL)
    {
      delete [] str;
      str = NULL;
    }
    // 将右变对象的值拷贝给左边对象,深拷贝
    size = obj.size;
    str = new char;
    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;
    strcpy(this->str, str);
    return *this;
}


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

2.8比较字符串对象大小

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


[*]比较字符串大小
// 重载 > 运算符
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) {
    if ((this->str == NULL) || (str == NULL))
    {
      cout << "存在空字符串,无法比较" << endl;
      exit(-1);
    }
    if (strcmp(this->str, str) > 0)
      return true;
    return false;
}


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



[*]代码演示
#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() {
    size = 0;
    str = NULL;
}

MyString::MyString(char *str) {
    size = strlen(str);
    // 多加一个放 \0
    this->str = new char;
    // strcpy 会拷贝 \0
    strcpy(this->str, str);
}

MyString::MyString(const MyString &obj) {
    size = obj.size;
    this->str = new char;
    strcpy(str, obj.str);
}

MyString::~MyString() {
    if (str != NULL)
    {
      delete [] str;
      str = NULL;
    }
}
int MyString::Size() {
    return size;
}
// 打印字符串,重载 << 运算符
ostream& operator<<(ostream &out, MyString obj)
{
    out << obj.str;
    return out;
}
// 输入字符串,重载 >> 运算符
istream& operator>>(istream &in, MyString &obj)
{
    // 如果字符串不为空,先指向空
    if (obj.str != NULL)
    {
      delete [] obj.str;
      obj.str = NULL;
    }

    // 定义一个临时变量存放键盘输入的值
    char temp = "";
    in >> temp;
    obj.size = strlen(temp);
    obj.str = new char;
    strcpy(obj.str, temp);
    return in;
}
char &MyString::operator[](int index) {
    // 先判断是否为空
    if (NULL == str)
    {
      cout << "字符串为空,无法操作" << endl;
      exit(-1);
    }

    // 判断索引是否越界
    if (index >=0 && index <size)
    {
      return str;
    }
    else
    {
      cout << index << "越界" << endl;
      exit(-1);
    }
}
MyString &MyString::operator=(MyString &obj) {    // 先判断左值是否为空,非空要先开释    if (str != NULL)    {      delete [] str;      str = NULL;    }    // 将右变对象的值拷贝给左边对象,深拷贝    size = obj.size;    str = new char;    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;    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) {
    if ((this->str == NULL) || (str == NULL))
    {
      cout << "存在空字符串,无法比较" << endl;
      exit(-1);
    }
    if (strcmp(this->str, str) > 0)
      return true;
    return false;
}
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 << endl;    str3 = '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;}

[*]运行效果
------------------- 重载<< ------------------
hello world!
size = 12
------------------- 重载>> ------------------
请输入一个字符串(不带空格):helloworld
helloworld
------------------- 重载[] ------------------      
o
hEllo
------------------- 重载= ------------------
hello human
hello human
------------------- 重载比较运算符 ------------------
请输入两个字符串:hallo hello
hallo小于hello
三、其它知识点补充

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

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


[*]代码演示
class Complex {
public:
    int flag;
   
    // 构造函数
    Complex(int flag) {
      this->flag = flag;
    }
    // 重载 += 运算符
    Complex &operator+=(Complex &complex) {
      this->flag = this->flag + complex.flag;
      return *this;
    }
    // 重载 && 运算符
    bool operator&&(Complex &complex) {
      return this->flag && complex.flag;
    }
};

int main() {
    Complex complex1(0); //flag 0
    Complex complex2(1); //flag 1

    if (complex1 && (complex1 += complex2)) {
      cout << "真!
" << endl;
    } else {
      cout << "假!" << endl;
    }
    return 0;
}


[*]运行效果
真!


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

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

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


[*]代码演示
class Data {
private:
    int num;
public:
    Data() {
      cout << "Data的无参构造" << endl;
    }

    Data(int num) {
      this->num = num;
      cout << "Data的有参构造 num=" << num << endl;
    }

    void showData() {
      cout << "num=" << num << endl;
    }

    ~Data() {
      cout << "Data的析构函数" << endl;
    }
};

class SmartPoint {
private:
    Data *sp;
public:
    SmartPoint() : sp(NULL) {}

    SmartPoint(Data *sp) {
      this->sp = sp;
    }

    SmartPoint(const SmartPoint &ob) {
      this->sp = new Data;
      *sp = *ob.sp;
    }

    ~SmartPoint() {
      delete sp;
    }

    Data *operator->() {
      return sp;
    }

    Data &operator*() {
      return *sp;
    }
};

int main() {
    SmartPoint bp(new Data(100));
    bp->showData();
    (*bp).showData();
    return 0;
}


[*]运行效果
Data的有参构造 num=100
num=100               
num=100               
Data的析构函数

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 06-C++类和对象强化