1-2 友元、运算符重载和继承

1 友元

目的是让一个函数或者类可以访问另一个类的私有成员,其关键字为friend

三种实现:

  • 全局函数做友元
    在类外定义一个全局函数,在类内的最上方将其声明为友元函数,声明的时候不需要放在public或private中,如:
friend void goodGay(Building &building);
  • 类做友元
    将允许访问本类私有属性的类在最上面声明为friend
friend class goodGay;
  • 成员函数做友元
    将某一类允许访问本类私有属性的成员函数在最上面声明为friend
friend void GoodGay::visit();

2 运算符重载

对已有运算符重新定义,赋予其另一种功能,以适应不同的数据类型,比如两个对象实现相加

1 重载加号运算符+

成员函数重载,这个时候调用者就是符号左边的对象

class Person
{
public:
    int m_A;
    int m_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;
    }

};

调用时直接:
Person p3 = p1 + p2;//p1.operator+(p2);

全局函数重载,这个时候它是作为一个函数来使用的,不属于任何对象,传入参数时有要求,第一个参数就是运算符左边的参数,第二个参数就是运算符右边的参数

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 p3 = p1 + p2;//operator+(p1,p2);

2 重载输出运算符<<

使得能够输出任意类型,比如可以直接对某一对象的属性进行输出,比如cout << p;

要注意在这里是无法进行成员函数调用的,调用者是对象,而输出的时候相当于是cout调用了<<,只能进行全局函数的重载

ostream &operator<<(ostream &cout, Person &p)//流对象不能拷贝,只能地址传递
{
    cout<<p.m_A<<" "<<p.m_B<<endl;
    return cout;
}

为满足链式编程思想,即<<可重复调用,返回值应该设置为cout类型

3 重载递增运算符++

让对象也可以实现自增

重载前置++

//以引用的形式返回对象本身,因为我们对于一个对象可能会有多次自增操作,引用确保
我们每次操作的是同一个对象,否则就是拷贝形式的返回,返回的是一个新对象
MyInteger &operator++(){
        m_NUm++;
        return *this;//返回这个对象本身
    }

重载后置++

//用一个占位参数来实现函数重载,这里不能返回引用了,因为局部对象不允许返回引用
    MyInteger operator++(int){
        //先记录自增前的结果
        MyInteger temp = *this;
        //再递增
        m_NUm++;
        //返回记录结果
        return temp;
    }

在进行自增时,我们调用++得到的是返回结果,也就是先前的值,然后再次输出时得到的就是加1的结果

4 重载赋值运算符=

C++编译器对一个类还提供第四个默认函数,就是operator=,其可以进行对象间的浅拷贝,但是如果对象有堆区数据,在释放的时候会进行重复释放,这个时候可以进行重载=运算符

int *m_Age;

Person &operator=(Person &p){
    if(m_Age!=NULL){
        delet m_Age;//先判断是否有属性在堆区,如果有先释放干净,再深拷贝
        m_Age = NULL;
    }
    m_Age = new int(*p.m_Age);
    return *this;
}

同样为了实现连等操作,需要返回对象本身

5 重载关系运算符==

目的是可以实现对象之间的比较

bool operator==(Person &p){
        if(this->m_name == p.m_name && this->m_age == p.m_age){
            return true;
        }
        return false;
    }

6 重载函数调用运算符()

由于重载之后对象可以像函数一样使用,这样的对象又称为仿函数

class MyPrint
{
public:
    //重载函数调用运算符
    void operator()(string test){
        cout<<test<<endl;
    }
};

void test01(){
    MyPrint myPrint;//相当于创建了一个仿函数
    myPrint("hello");
}

int main()
{
    test01();
    return 0;
}

匿名函数对象,如MyPrint("hello");中,MyPrint()操作就相当于创建了一个匿名对象,其特点是当前行执行完后立即释放掉

3 继承

利用继承的技术可以实现代码的复用

class A : 继承方式 B

A称作子类或派生类,B称作父类或基类

1 继承方式

  • 公共继承:父类的公共和保护到了子类还是各自的属性
  • 保护继承:父类的公共和保护到了子类全变成了保护属性
  • 私有继承:父类的公共和保护到了子类全变成了私有属性

父类私有无论哪一种继承,子类均继承不到,无法访问

2 继承中的对象模型

继承时会继承父类的所有非静态成员(静态只有一份,子类父类共享),只是有些成员访问不到而已,可以通过sizeof来查看子类和父类的大小

3 继承中的构造和析构顺序

父类构造
子类构造
子类析构
父类析构

4 继承中同名成员的处理

对于同名成员的访问:

  • 默认是访问子类的成员,子类会隐藏同名的父类成员
  • 如果要访问父类成员要加上作用域::

无论是静态还是非静态都一样

5 多继承

C++允许一个类继承多个类,语法:

class 子类:继承方式 父类1,继承方式,父类2,...

实际开发中不建议多继承

6 菱形继承

两个派生类继承同一个基类,又有一个类同时继承两个派生类,这种继承方式被称作菱形继承

这种方式有两个缺点:

  • 二义性,如果两个派生类有同名函数,那么继承过来使用二义
  • 两个派生类中从基类继承来的成员变量在最终的类中无法区分,只能加以作用域来区分

解决方法:虚继承

class animal{};//该类被称为虚基类
class sheep:virtual public animal{};//虚继承
class tuo:virtual public animal{};//虚继承
class sheeptuo:public sheep, public tuo{};

这样在sheeptuo类中就不会再出现两份成员变量了,只存在一份

posted @ 2023-08-06 20:55  白日梦想家-c  阅读(10)  评论(0编辑  收藏  举报