C++语法基础(C)- 类封装和函数重载

本文详细讲解了C++中的类封装,特别是如何重载数组的[]运算符以及自定义类型的运算符如加法、左移、赋值等。同时涉及智能指针的指针运算符重载,以及如何处理浅拷贝和深拷贝的问题,以及字符串类的封装和相关运算符重载。
摘要由CSDN通过智能技术生成

类封装和运算符重载

(一)数组类封装及重载[]运算符

重载[]运算符:可以通过[]设置和获取数据

//MyArray.h
class MyArray//指处理int类型
{
public:
    //默认构造
    MyArray();
    //有参构造
    MyArray(int capacity);
    //拷贝构造
    MyArray(const MyArray &arr);
    //尾插
    void pushBack(int val);
    //根据位置设置数据
    void setData(int index, int val);
    //根据位置获取数据
    int getData(int index);
    //获取数组大小
    int getSize();
    //获取数组容量
    int getCapacity();
    // 重载[]运算符,这里需要返回引用,因为函数调用不能作为左值(arr[] = 1000;不能使用)
    int& operator[] (int index);
    //析构函数
    ~MyArray();
private:
    //指向堆区的数据指针
    int* pAddress;
    //数组容量
    int m_Capacity;
    //数组大小
    int m_Size;
};
//MyArray.cpp
#include "MyArray.h"
MyArray::MyArray()
{
    cout << "默认构造函数调用" << endl;
    this->m_Capacity = 100;
    this->m_Size = 0;
    this->pAddress = new int[this->m_Capacity];
}
MyArray::MyArray(int capacity)
{
    cout << "有参构造函数调用" << endl;
    this->m_Capacity = capacity;
    this->m_Size = 0;
    this->pAddress = new int[this->m_Capacity];
}
MyArray::MyArray(const MyArray& arr)
{
    cout << "拷贝构造函数调用" << endl;
    this->m_Size = arr.m_Size;//注:这里可以访问arr.m_Size(private),同类可以访问私有内容
    this->m_Capacity = arr.m_Capacity;

    //深拷贝
    this->pAddress = new int[arr.m_Capacity];
    for (int i = 0; i < arr.m_Size; i++)
    {
        this->pAddress[i] = arr.pAddress[i]; 
    }
}
void MyArray::pushBack(int val)
{
    this->pAddress[this->m_Size] = val;
    this->m_Size++; 
}
void MyArray::setData(int index, int val)
{
    this->pAddress[index] = val;
}
int MyArray::getData(int index)
{
    return this->pAddress[index];
}
int MyArray::getSize()
{
    return this->m_Size;
}
int MyArray::getCapacity()
{
    return this->m_Capacity;
}
int& MyArray::operator[] (int index)
{
    return this->pAddress[index];
}
MyArray::~MyArray()
{
    cout << "析构函数调用" << endl;
    if (this->pAddress != NULL)
    {
        delete[] this->pAddress;
        this->pAddress = NULL;
    }
}

(二)运算符重载

问题:
对于内置类型,编译器知道运算符应该如何计算(不可能改变),但对于自定义数据类型,编译器并不知道;

说明:
运算符重载(operator overloading)只是一种语法上的方便,也就是它只是另一种函数调用的方式。

  • 利用成员函数进行加号运算符重载
  • 利用全局函数进行加号运算符重载
  • 运算符重载可以进行函数重载

语法:

  • 重载运算符被定义为全局函数:对于一元是一个参数,对于二元是两个参数
  • 重载运算符被定义为成员函数:对于一元没有参数,对于二元是一个参数

1.加号运算符重载

目的: 可以用+进行对象间相加

class Person
{
public:
    Person() {}
    Person(int a, int b):m_A(a),m_B(b) {}
    //利用成员函数进行 加号运算符重载
    Person operator+(Person &p)
    {
        Person temp;
        temp.m_A = this->m_A + p.m_A;
        temp.m_B = this->m_B + p.m_B;
        return temp;
    }
    int m_A;
    int m_B;
};

//利用全局函数进行 加号运算符重载
Person operator+(Person& p1, Person& p2)
{
    Person temp;
    temp.m_A = p1.m_A + p2.m_A;
    temp.m_B = p1.m_B + p2.m_B;
    return temp;
}

//加号运算符重载 进行函数重载
Person operator+(Person& p, int a)
{
    Person temp;
    temp.m_A = p.m_A + a;
    temp.m_B = p.m_B + a;
    return temp;
}

2.左移运算符重载

目的: 可以使用cout <<<< endl输出对象

class Person
{
    //将全局函数-左移运算符重载变为Person的友元函数,可以使成员变量私有化
    friend ostream& operator<< (ostream& cout, Person& p);
public:
    Person() {};
    Person(int a, int b)
    {
        this->m_A = a;
        this->m_B = b;
    }
    //void operator<<(ostream &cout){}左移运算符重载只能使用全局函数才能出现cout<<
private:
    int m_A;
    int m_B;
};
//返回值需要是ostream,才能使用endl
ostream& operator<< (ostream& cout, Person& p)
{
    cout << "p.m_A:" <<p.m_A << "p.m_B:" << p.m_B;
    return cout;
}

3.前置++后置++运算符重载

目的: 自己写的MyInteger对象可以使用前置++和后置++

前置++和后置++优先使用前置++

class MyInteger
{
friend ostream& operator<<(ostream& cout, const MyInteger& myInteger);
public:
    //默认构造函数
    MyInteger() { m_Int = 0; }
    //前置++:这里只能返回引用,返回值多次使用++导致只使用了一次++
    MyInteger& operator++()
    {
        //先 + 1
        m_Int = m_Int + 1;
        //后返回
        return *this;
    }
    //后置++:这里只能返回值,因为返回的是临时变量,属于局部变量
    MyInteger operator++(int)
    {
        //先 保留当前状态
        MyInteger temp = *this;
        //后 +1 
        m_Int = m_Int + 1;
        //再返回
        return temp;
    }
private:
    int m_Int;
};

//这里的const:(E0349没有与这些操作数匹配的"<<"运算符)
//如果返回的是一个临时变量,临时变量属于纯右值,它不能绑定到左值引用,你需要使用常量左值引用或右值引用来绑定
ostream& operator<<(ostream& cout,const MyInteger& myInteger) 
{
    cout << "m_Int:" <<myInteger.m_Int;
    return cout;
}
void test01()
{
    MyInteger myinteger;
    cout << ++(++myinteger) << endl;
    cout << myinteger << endl;
}
void test02()
{
    MyInteger myinteger;
    cout << myinteger++ << endl;
    cout << myinteger << endl;
}

4.指针运算符重载-智能指针

目的: 用来托管new出来的对象的释放
如果想智能指针对象像指针一样调用变量和函数,需要重载->*

class Person
{
public:
    Person()
    {
        m_Age = 0;
    }
    Person(int age)
    {
        this->m_Age = age;
    }
    ~Person(){}
    void showAge()
    {
        cout << "m_Age: " << this->m_Age << endl;
    }
private:
    int m_Age;
};
class SmartPointer//智能指针
{
public:
    SmartPointer(Person* person)
    {
        this->person = person;
    }
    ~SmartPointer()
    {
        if (this->person != NULL)
        {
            delete this->person;
            this->person = NULL;
        }
    }
    //重载->运算符,返回指针
    Person* operator->()
    {
        return this->person;
    }
    //重载*运算符,返回本体-引用
    Person& operator*()
    {
        return (*this->person);
    }
private:
    Person* person;
};
void test()
{
    /*Person* person = new Person();
    person->showAge();
    (*person).showAge();
    delete person;*/

    SmartPointer sp = SmartPointer(new Person(18));
    //如果项sp对象像指针一样调用变量和函数,需要重载->和*
    sp->showAge();//sp->->showAge();编译器简化为sp->showAge();
    (*sp).showAge();
}

5.赋值运算符=重载

目的: 为了处理类中默认的=的运算符的浅拷贝问题

  • 系统会默认给一个类添加4个函数:默认构造、析构、拷贝构造(浅拷贝)、operator=(浅拷贝)
  • 由于系统提供的operator=会进行简单的值拷贝,如果成员属性中有堆区的数据,会导致重复释放–需要重载operator=
class Person
{
public:
    Person(const char* name, int age)
    {
        this->m_Age = age;
        this->m_Name = new char[strlen(name) + 1];
        strcpy(this->m_Name,name);
    }
    Person(const Person &p)
    {
        this->m_Name = new char[strlen(p.m_Name) + 1];
        strcpy(this->m_Name, p.m_Name);
        this->m_Age = p.m_Age;
    }
    //系统会默认给一个类创建至少3个函数:默认构造、析构、拷贝构造(浅拷贝)、operator=(浅拷贝)
    Person& operator=(const Person &person)
    {
        //先判断原来的堆区是否有数据,如果有先释放
        if (this->m_Name != NULL)
        {
            delete[] this->m_Name;
            this->m_Name = NULL;
        }
        //再重新创建name
        this->m_Name = new char[strlen(person.m_Name) + 1];
        strcpy(this->m_Name, person.m_Name);
        this->m_Age = person.m_Age;
        return *this;
    }
    ~Person()
    {
        if(this->m_Name != NULL)
        {
            delete [] this->m_Name;//这里是数组,[]不能丢
            this->m_Name = NULL;
        }
    }
    int m_Age;
    char *m_Name;//注:这里的值是堆区的数据,拷贝构造函数和operator=都存在浅拷贝问题
};
void test()
{
    Person p1("A", 10);
    Person p2("B", 20);
    p1 = p2;//这里需要重载=运算符
    cout << "p1.m_Name" << p1.m_Name << "age:" << p1.m_Age << endl;
    cout << "p2.m_Name" << p2.m_Name << "age:" << p2.m_Age << endl;
    Person p3(" ", 0);
    p3 = p1 = p2;//这里的重载的=运算符 需要返回对象
    cout << "p3.m_Name" << p3.m_Name << "age:" << p3.m_Age << endl;
    Person p4(p3);
    cout << "p4.m_Name" << p4.m_Name << "age:" << p4.m_Age << endl;
}

6.关系运算符==重载

class Person
{
public:
    Person(string name, int age)
    {
        this->name = name;
        this->age = age;
    }
    bool operator== (Person &p)//重载==运算符
    {
        return this->name == p.name && this->age == p.age;
    }
private:
    string name;
    int age;
};

7.函数调用运算符()重载-仿函数

class Myfunc
{
public:
    void operator() (string s)
    {
        cout << s << endl;
    }
};
void test()
{
    Myfunc myfunc;//STL 仿函数
    myfunc("");
}

8.符号重载总结

  • = [] () ->运算符只能通过成员函数进行重载
  • << >>运算符只能通过全局函数结合友元函数进行重载
  • 不要重载&& || 运算符,因为无法实现短路规则

(三)字符串类封装

  1. 设计类MyString
  2. 成员属性
    • char* pString 维护指向堆区指针
    • int m_Size 字符串长度
  3. 成员函数
    • 有参构造
    • 拷贝构造
    • 析构
  4. <<重载
  5. >>重载
  6. []重载 进行[]字符读写
  7. =重载 防止浅拷贝问题出现
  8. +重载 字符串拼接
  9. ==重载 对比
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值