C++ 运算符重载(二)(11)

2022-06-24  本文已影响0人  maskerII

1. 赋值运算符重载

  1. 编译器默认给类提供了一个默认的赋值运算符重载函数
  2. 默认的赋值运算符重载函数做了简单的赋值操作
  3. 当类有成员指针时,并且在构造函数中申请堆区空间,在析构函数中释放堆区空间,如果不重写赋值运算符重载函数,会导致内存泄漏,和同一块堆区空间被释放多次
  4. 赋值运算符中 为什么要返回引用 如果赋值运算符返回值,在复数运算时违背了它本来的寓意
class Maker1
{
public:
    int id;
    int age;

public:
    void printMaker1()
    {
        cout << "id = " << this->id << " age = " << this->age << endl;
    }

public:
    Maker1()
    {
    }
    Maker1(int id, int age)
    {
        this->id = id;
        this->age = age;
    }
};

void test01()
{
    Maker1 m1(18, 20);
    Maker1 m2;
    m2 = m1; // 赋值操作  默认的赋值运算符重载函数做了简单的赋值操作

    m2.printMaker1();
}

class Student
{
public:
    char *pName;

public:
    void printStudent()
    {
        cout << "name =  " << this->pName << endl;
    }

public:
    Student(const char *name)
    {
        cout << "有参构造函数" << endl;
        // 申请堆区空间
        this->pName = new char[strlen(name) + 1];
        // 拷贝数据
        strcpy(this->pName, name);
    }
    // 防止浅拷贝
    Student(const Student &m)
    {
        cout << "拷贝构造函数" << endl;
        // 申请堆区空间
        pName = new char[strlen(m.pName) + 1];
        // 拷贝数据
        strcpy(pName, m.pName);
    }
    // 重写赋值运算符重载
    // 返回引用
    Student &operator=(const Student &stu)
    {
        cout << "赋值运算" << endl;
        // 不能确定this->pName指向的空间是否能装上stu中的数据,所以先释放this->pName指向的空间,再申请空间,拷贝数据,最后返回*this
        // 1.删除原来的空间
        if (this->pName != NULL)
        {
            delete[] this->pName;
            this->pName = NULL;
        }
        // 2.申请空间
        this->pName = new char[strlen(stu.pName) + 1];
        // 3.拷贝数据
        strcpy(this->pName, stu.pName);
        // 4.返回对象本身
        return *this;
    }

    ~Student()
    {
        cout << "析构函数" << endl;
        if (pName != NULL)
        {
            delete[] pName;
            pName = NULL;
        }
    }
};

void test02()
{
    Student m1("Emily");
    Student m2 = m1; // 拷贝构造
    m2.printStudent();

    cout << "============== 1" << endl;
    Student m3("悟空");
    m3 = m1; // 赋值元算符
    m3.printStudent();

    cout << "============== 2" << endl;

    Student m4 = m3 = m1;
    m3.printStudent();

    cout << "------------- 3" << endl;
}

为什么operator=返回一个reference to *this ?

为了实现连续赋值,赋值操作符必须返回一个引用指向操作符的左侧实参。这是你为class实现赋值操作符必须遵循的协议。这个协议不仅适用于标准的赋值形式,也适用于+=、-=、*=等等。

2. 关系运算符重载 == !=

// == !=
class Maker
{
private:
    int id;
    int age;

public:
    Maker(int id, int age);
    bool operator==(Maker &m2);
    bool operator!=(Maker &m2);
};

Maker::Maker(int id, int age)
{
    this->id = id;
    this->age = age;
}

bool Maker::operator==(Maker &m2)
{
    if (this->id == m2.id && this->age == m2.age)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool Maker::operator!=(Maker &m2)
{
    if (this->id != m2.id || this->age != m2.age)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void test01()
{
    Maker m1(18, 20);
    Maker m2(25, 56);

    if (m1 == m2)
    {
        cout << "真" << endl;
    }
    else
    {
        cout << "假" << endl;
    }

    if (m1 != m2)
    {
        cout << "真" << endl;
    }
    else
    {
        cout << "假" << endl;
    }
}

3. 前置加加和后置加加运算符重载

class Maker
{
public:
    int age;

public:
    Maker(int age)
    {
        this->age = age;
    }
    // 前置加加
    Maker &operator++()
    {
        cout << "前置加加" << endl;
        ++this->age;
        return *this;
    }
    // 后置加加
    // 前置加加和后置加加以是否有占位符区分
    Maker operator++(int) // 占位参数必须是int
    {
        // 后置加加 先返回 再加加
        cout << "后置加加" << endl;
        Maker tmp(*this); // 拷贝构造
        ++this->age;
        return tmp;
    }
};

// << 运算符重载
// 注意 此处 形参Maker m,如果使用引用,cout << m1++ << endl 会出错
// 原因 m1++ 后置加加是返回了一个临时对象,临时对象引用会出错
ostream &operator<<(ostream &os, Maker m)
{
    cout << m.age << endl;
    return os;
}

void test01()
{
    Maker m1(1);
    cout << m1 << endl; // 1

    cout << ++m1 << endl; // 2

    cout << (m1++).age << endl; // 2

    cout << m1++ << endl; // 3

    cout << m1 << endl; // 4
}

4. 数组下标运算符重载[]


class MyArray
{
public:
    // 头插
    void PushFront(int val)
    {
        if (mCapacity == mSize)
        {
            return;
        }

        // 数据往后移
        for (int i = mSize - 1; i >= 0; I--)
        {
            pArray[i + 1] = pArray[I];
        }

        pArray[0] = val;

        mSize++;
    }
    // 尾插
    void PushBack(int val)
    {
        if (mCapacity == mSize)
        {
            return;
        }

        pArray[mSize] = val;

        mSize++;
    }
    // 头删
    void popFront()
    {
        if (mSize == 0)
        {
            return;
        }
        // 后面的数往前面移动来覆盖第一个元素
        for (int i = 0; i < mSize - 1; I++)
        {
            pArray[i] = pArray[i + 1];
        }

        mSize--;
    }
    // 尾删
    void popBack()
    {
        if (mSize == 0)
        {
            return;
        }
        mSize--;
    }

    // 获取数组元素的个数
    int Size()
    {
        return mSize;
    }

    // 获取数组的容量
    int Capacity()
    {
        return mCapacity;
    }

    // 指定位置插入元素
    void Insert(int pos, int val)
    {
        if (mCapacity == mSize)
        {
            return;
        }

        if (pos < 0 || pos > mSize - 1)
        {
            return;
        }

        for (int i = mSize - 1; i >= pos; I--)
        {
            pArray[i + 1] = pArray[I];
        }

        pArray[pos] = val;

        mSize++;
    }

    // 获取指定位置的值
    int &Get(int pos)
    {
        if (pos >= 0 && pos <= mSize - 1)
        {
            return pArray[pos];
        }
    }

    // 在指定位置修改值
    void Set(int pos, int val)
    {
        if (pos < 0 || pos > mSize - 1)
        {
            return;
        }

        pArray[pos] = val;
    }

public:
    MyArray()
    {
        // 无参数构造
        mCapacity = 20;
        mSize = 0;
        pArray = new int[mCapacity];
    }

    MyArray(int capacity, int val = 0)
    {
        mCapacity = capacity;
        mSize = capacity;

        pArray = new int[mCapacity];

        for (int i = 0; i < mSize; I++)
        {
            pArray[i] = val;
        }
    }

    MyArray(const MyArray &m)
    {
        mSize = m.mSize;
        mCapacity = m.mCapacity;
        // 申请空间
        pArray = new int[mCapacity];
        // 拷贝数据
        for (int i = 0; i < mSize; I++)
        {
            pArray[i] = m.pArray[I];
        }
    }
    // 重写赋值运算符重载函数
    MyArray &operator=(const MyArray &m)
    {
        // 1. 删除原来空间
        if (this->pArray != NULL)
        {
            delete[] pArray;
            pArray = NULL;
        }

        this->mCapacity = m.mCapacity;
        this->mSize = m.mSize;

        // 2. 申请堆区空间
        this->pArray = new int[this->mCapacity];

        // 3.拷贝数据
        for (int i = 0; i < this->mSize; I++)
        {
            this->pArray[i] = m.pArray[I];
        }

        // 4.返回对象本身
        return *this;
    }
    // 数组下标运算符重载
    int &operator[](int pos)
    {
        // 赋值时 mSize加加
        if (this->mSize <= pos){
            this->mSize ++;
        }
        return this->pArray[pos];
    }

    ~MyArray()
    {
        if (pArray != NULL)
        {
            delete[] pArray;
            pArray = NULL;
        }
    }

private:
    int mCapacity; // 容量
    int mSize;     // 元素个数
    int *pArray;   // 指向堆区空间,存储数据
};

void test01()
{
    MyArray arr;
    for (int i = 0; i < 20; I++)
    {
        arr[i] = i + 10;
    }

    for (int i = 0; i < 20; I++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

void test02()
{
    MyArray arr;
    for (int i = 0; i < 20; I++)
    {
        arr[i] = i + 10;
    }

    MyArray arr2; //无参构造
    arr2 = arr; // 赋值运算

    for (int i = 0; i < 20; I++)
    {
        cout << arr2[i] << " ";
    }
    cout << endl;

}

5. 指针运算符(*、->)重载

class Person{
public:
    Person(int param){
        this->mParam = param;
    }
    void PrintPerson(){
        cout << "Param:" << mParam << endl;
    }
private:
    int mParam;
};

class SmartPointer{
public:
    SmartPointer(Person* person){
        this->pPerson = person;
    }
    //重载指针的->、*操作符
    Person* operator->(){
        return pPerson;
    }
    Person& operator*(){
        return *pPerson;
    }
    ~SmartPointer(){
        if (pPerson != NULL){
            delete pPerson;
        }
    }
public:
    Person* pPerson;
};

void test01(){
    
    //Person* person = new Person(100);
    //如果忘记释放,那么就会造成内存泄漏

    SmartPointer pointer(new Person(100));
    pointer->PrintPerson();
}
// 智能指针类是管理另一个类的对象的释放

class Maker
{
public:
    Maker()
    {
        cout << "Maker 无参构造" << endl;
    }
    void printMaker()
    {
        cout << "Hello Maker" << endl;
    }
    ~Maker()
    {
        cout << "Maker 析构函数" << endl;
    }
};

class SmartPoint
{
private:
    Maker *pMaker;
public:
    // ->指针运算符重载
    Maker* operator->() {
        return this->pMaker;
    }
    // *星花运算符重载
    Maker& operator*() {
        return *(this->pMaker);
    }
public:
    SmartPoint(Maker *m)
    {
        cout << "SmartPoint 有参构造函数" << endl;
        pMaker = m;
    };
    ~SmartPoint()
    {
        cout << "SmartPoint 析构函数" << endl;
        if (this->pMaker != NULL)
        {
            delete this->pMaker;
            this->pMaker = NULL;
        }
    };
};

void test01()
{
    Maker *p = new Maker;
    SmartPoint sm(p); // 栈区,test01()函数执行完后,会调用SmartPoint的析构函数
    // 当test01()函数结束时,会调用SmartPoint的析构函数,在这个析构函数中delete了Maker的对象,就会调用Maker的析构函数

    // (sm->)->printMaker() 编译器优化了
    // sm-> 类似于 pMaker->
    sm->printMaker();

    (*sm).printMaker();
}

6. 函数调用符号() 重载(仿函数)

如果一个类重载了函数调用符号,那么这个类实例化出来的对象也叫仿函数
仿函数的作用: 1. 方便代码维护 2.方便有权限的调用函数 3. 作为算法的策略

// 重载函数调用符号
// 如果一个类重载了函数调用符号,那么这个类实例化出来的对象也叫仿函数
// 仿函数的作用: 1. 方便代码维护 2.方便有权限的调用函数 3. 作为算法的策略(后讲)
class Maker
{
public:
    string name;

public:
    // 第一个括号是表示我们要重载函数调用符号,第二个符号表示这是个函数
    void operator()()
    {
        cout << "Hello Maker" << endl;
    }
    void operator()(int val1,int val2){
        cout << val1 + val2 << endl;
    }

    void printMaker()
    {
        cout << name + "你好漂亮" << endl;
    }

public:
    Maker()
    {
        cout << "无参构造函数" << endl;
        name = "Emily";
    }
};

void test01()
{
    Maker m; // 无参构造 m是对象
    m(); // 看起来像函数,其实m是对象 void operator()()
    m(10,20); // 调用的是函数调用符号重载函数 void operator()(int val1,int val2)
    m.printMaker();
}

7. bool 和 ! 运算符重载

class Maker
{
private:
    int a;

public:
    void SetA(int a)
    {
        this->a = a;
    }
    // 重载bool运算符符
    // 无返回值 无void
    operator bool()
    {
        if (a > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    // 重载!运算符
    bool operator!()
    {
        if (a > 0)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    // 重载类型(int)运算符
    // 无返回值
    operator int() {
        return 10;
    }

public:
    Maker()
    {
        a = 0;
    };
    ~Maker(){

    };
};

void test01()
{
    Maker m;
    m.SetA(10);
    if (m)
    {
        cout << "真" << endl;
    }
    else
    {
        cout << "假" << endl;
    }

    if (!m)
    {
        cout << "真" << endl;
    }
    else
    {
        cout << "假" << endl;
    }

    int b = (int)m;
    cout << b << endl;
}

8. 运算符和结合性

image.png image.png image.png image.png

9. 强化训练-字符串的封装

MyString.h

#pragma once
#include <iostream>
using namespace std;

class MyString
{
    friend ostream &operator<<(ostream &os, MyString &str);
    friend istream &operator>>(istream &is, MyString &str);

public:
    MyString();
    MyString(int n, char c = 'a'); // 用户可以设定初始化字符串,n个c组成的字符串
    MyString(const MyString &str);
    MyString &operator=(const MyString &str);
    MyString operator+(const MyString &str);
    MyString operator+(const char *s);
    MyString &operator+=(const MyString &str);
    MyString &operator+=(const char *s);
    char &operator[](int index);
    int Size();
    ~MyString();

private:
    char *pStr; // 指向堆区空间
    int mSize;
};
ostream &operator<<(ostream &os, MyString &str);
istream &operator>>(istream &is, MyString &str);

MyString.cpp

#include "MyString.h"

MyString::MyString()
{
    cout << "无参构造函数" << endl;
    this->pStr = new char[1];
    this->pStr[0] = '\0';
    this->mSize = 0;
}
MyString::MyString(int n, char c)
{
    cout << "有参构造函数" << endl;
    this->pStr = new char[n + 1];
    for (int i = 0; i < n; i++)
    {
        this->pStr[i] = c;
    }
    // 结束字符'\0'
    // 防止被烫着
    this->pStr[n] = '\0';

    this->mSize = n;
}
MyString::MyString(const MyString &str)
{
    cout << "拷贝构造函数" << endl;
    this->pStr = new char[strlen(str.pStr) + 1];
    strcpy(this->pStr, str.pStr);
    this->mSize = str.mSize;
}
MyString &MyString::operator=(const MyString &str)
{
    cout << "运算符重载 =" << endl;
    // 1.释放原来空间
    if (this->pStr != NULL)
    {
        delete[] this->pStr;
        this->pStr = NULL;
    }

    // 2.申请空间
    this->pStr = new char[strlen(str.pStr) + 1];
    // 3.拷贝数据
    strcpy(this->pStr, str.pStr);

    this->mSize = str.mSize;

    // 返回对象本身
    return *this;
}
MyString MyString::operator+(const MyString &str)
{
    cout << "运算符重载 + MyString" << endl;
    // MyString s3=s1+s2
    // 获取s3要开辟的空间大小
    int newlen = this->mSize + str.mSize + 1;

    // 1.定义一个临时变量
    MyString tmp;

    // 2.释放原来空间
    if (tmp.pStr != NULL)
    {
        delete[] tmp.pStr;
        tmp.pStr = NULL;
    }
    // 3.申请新的空间
    tmp.pStr = new char[newlen];
    // 清除下空间
    memset(tmp.pStr, 0, newlen);

    tmp.mSize = this->mSize + str.mSize;

    // 4.追加字符到空间中
    strcat(tmp.pStr, this->pStr);
    strcat(tmp.pStr, str.pStr);

    // 5. 返回临时变量
    return tmp;
}
MyString MyString::operator+(const char *s)
{
    cout << "运算符重载 + Char" << endl;
    int newlen = this->mSize + strlen(s);
    // 开辟空间
    char *newspace = new char[newlen + 1];
    memset(newspace, 0, newlen + 1);

    // 追加数据到空间
    strcat(newspace, this->pStr);
    strcat(newspace, s);

    MyString tmp;
    if (tmp.pStr != NULL)
    {
        delete[] tmp.pStr;
        tmp.pStr = NULL;
    }

    tmp.pStr = newspace;

    tmp.mSize = newlen;
    
    return tmp;


}
MyString &MyString::operator+=(const MyString &str)
{
    cout << "运算符重载 += MyString" << endl;
    int newlen = this->mSize + strlen(str.pStr);

    // 1.开辟空间
    char * newspace = new char[newlen + 1];
    memset(newspace,0, newlen+1);
    // 2.追加数据
    strcat(newspace, this->pStr);
    strcat(newspace, str.pStr);

    // 3.释放原来空间
    if (this->pStr != NULL)
    {
        delete[] this->pStr;
        this->pStr = NULL;
    }
    // 4.指向newspace
    this->pStr = newspace;

    this->mSize = newlen;

    return *this;

    

}
MyString &MyString::operator+=(const char *s)
{
    cout << "运算符重载 += Char" << endl;
    int newlen = this->mSize + strlen(s);

    char *newspace = new char[newlen + 1];
    memset(newspace, 0, newlen+1);

    strcat(newspace, this->pStr);
    strcat(newspace, s);

    if (this->pStr != NULL)
    {
        delete[] this->pStr;
        this->pStr = NULL;
    }

    this->pStr = newspace;
    this->mSize = newlen;

    return *this;

}
char &MyString::operator[](int index)
{
    return this->pStr[index];
}
int MyString::Size()
{
    return mSize;
}
MyString::~MyString()
{
    if (this->pStr != NULL)
    {
        delete[] this->pStr;
        this->pStr = NULL;
    }
}

ostream &operator<<(ostream &os, MyString &str)
{
    os << str.pStr;
    return os;
}
istream &operator>>(istream &is, MyString &str)
{
    // 1. 定义临时空间
    char tmp[1024] = {0};
    // 2.获取用户输入的信息
    is>>tmp;
    // 3.释放原来空间
    if (str.pStr != NULL)
    {
        delete[] str.pStr;
        str.pStr = NULL;
    }

    // 4.申请新的空间
    str.pStr = new char[strlen(tmp) + 1];
    memset(str.pStr,0, strlen(tmp)+1);

    // 5.拷贝用户输入的信息
    strcpy(str.pStr,tmp);
    str.mSize = strlen(tmp);
    return is;

}
上一篇下一篇

猜你喜欢

热点阅读