寒假学习记录二(c++学习笔记)

一、运算符重载

运算符重载:对已有的运算符重载重新定义,赋予其另外一种功能,以适应不同的数据类型。

编译器定义的通用名称是operator需要重载的运算符。

重载时有两种方法:一是通过自己写成员函数实现,二是通过自己写全局函数实现

eg:

  1. 加号运算符重载

实现两个自定义数据类型相加的运算。

编译器定义了通用名称operator+。

方式1:通过自己写成员函数来实现两个对象相加属性后的新对象

class score {
public:
    int m_score;
    score operator+(score& two) {
        score three;
        three.m_score = this->m_score + two.m_score;
        return three;
    }
};
int main() {
    score one;
    one.m_score = 98;
    score two;
    two.m_score = 88;
    //score three= one.operator+(two);
    score three = one + two;
    cout<<"three的score是"<<three.m_score<<endl;
    system("pause");
    return 0;
}

该方法的本质调用是score three= one.operator+(two);

但是可以简化成score three = one + two;

方式2:通过自己写全局函数来实现两个对象相加属性后的新对象

class score {
public:
    int m_score;
};
score operator+(score& one, score& two) {
    score three;
    three.m_score = one.m_score + two.m_score;
    return three;
}
int main() {
    score one;
    one.m_score = 98;
    score two;
    two.m_score = 88;
    //score three= operator+(one,two);
    score three = one + two;
    cout << "three的score是" << three.m_score << endl;
    system("pause");
    return 0;
}

该方法的本质调用是score three= operator+(one,two);

也可以简化成score three = one + two;

2.左移运算符重载

实现输出自定义类型

输出时只使用一个一个自定义类型,无法使用自定义成员函数实现。

使用自定义全局函数实现输出自定义类型

class score {
public:
    int m_score;
};
ostream &operator<<(ostream& cout, score& one) {
    cout << "one的score是" << one.m_score;
    return cout;
}
int main() {
    score one;
    one.m_score = 98;
    //operator<<(cout, one);
    cout << one << endl;
    system("pause");
    return 0;
}

cout为输出流对象(ostream),全局只能有一个,所以传形参和全局函数的返回值类型设置时都使用引用,并且设置函数返回值为输出流,是为了能够使用链式输出在输出自定义函数后追加输出其他内容。

该方法的本质调用是operator<<(cout, one);

简化为cout << one << endl;

可以通过设置全局函数重载作友元的方式访问私有属性。

3.递增运算符重载

通过重载递增运算符,实现自定义整型数据的递增

class  myint {
public:
    myint() {
        num = 0;
    }
    myint& operator++(){//前置递增
        //先递增
        num++;
        //再返回
        return *this;
    }
    myint operator++(int) {//后置递增,int用来区分前后置
        //先记录
        myint temp = *this;
        //再递增
        num++;
        //最后返回记录的值
        return temp;
    }
    int num;
};
ostream &operator<<(ostream& cout, myint m_int) {
    cout  << m_int.num;
    return cout;
}
int main() {
    myint one;
    myint two;
    one.num = 1;
    two.num = 1;
    cout << ++one << endl;
    cout << two++ << endl;
    system("pause");
    return 0;
}

递增运算符是需要区分前后置,所以使用int作占位符来区分。

前置返回一个引用是为了能够实现在进行++(++one)时,是一直对一个数据进行操作。而在后置时不返回引用是因为返回的是一个局部参数,重载函数结束后就会结束,会出现非法操作。

4.函数调用运算符重载

函数调用运算符是(),没有固定写法,操作灵活。重载后调用像函数调用,所以又叫仿函数。

列举个仿printf

class  myprint {
public:
    void operator()(string test) {
        cout << test << endl;
    }
};

int main() {
    myprint Myprint;
    Myprint("hello world");
    system("pause");
    return 0;
}

二、继承

继承是面对对象的三大特征之一。

有些类与类之间有存在特殊关系,

上图中下一级类之间除了有上一级的共性之外也有自己的特征,所以利用这种关系,使用继承来实现减少重复代码的效果。

  1. 继承的基础语法

继承的好处就是减少代码重复

单继承:class 子类 : 继承方式 父类

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

简单案列:

//公共信息
class base {
public:
    void school() {
        cout << "xx学校" << endl;
    }
};
//个人信息
class member01:public base {
public:
    void name(){
        cout << "xx" << endl;
    }
};
class member02 :public base {
public:
    void name() {
        cout << "xxx" << endl;
    }
};
int main() {
    member01 one;
    member02 two;
    one.school();
    one.name();
    two.school();
    two.name();
    system("pause");
    return 0;
}

2.继承方式

继承的方式一共有3种:

·公共继承:父类中除了私有属性其他属性按原来权限继承。

·保护继承:父类中除了私有属性其他属性全部变为保护权限继承。

·私有继承:父类中除了私有属性其他属性全部变为私有权限继承。

父类中私有权限的属性不管哪种继承方式都不能继承(但是友元可以访问)

3.继承中的对象模型

父类中所有非静态成员属性都会被子类继承,其中私有属性虽然被编译器隐藏,不可访问,但是仍然会被继承。

4.继承中的构造和析构

先构造父类,再构造子类,析构时顺序相反

5.继承中同名成员处理

访问子类同名成员:直接访问

访问父类同名成员:需要加上作用域

(非静态与静态相同)

多继承中容易出现同名情况,要加上作用域来区分

6.菱形继承

有两个子类1和子类2同时继承某个父类,又有一个子类3同时继承这两个子类,就形成菱形继承。

菱形继承存在一个问题子类12从父类那继承来的共同属性会同时继承给子类3,会使该类中出现两份相同的属性。

解决方法:

虚继承:继承前加上关键字virtual,即class 子类:virtual 继承方式 父类

就可以使共同的属性变成一份。

三、多态

  1. 多态的基本概念

多态分为两类:

·静态多态:函数重载和运算符重载都属于静态多态。复用函数名

·动态多态:派生类和 虚函数实现运行时多态

静态多态和动态多态的区别:

静态多态的函数地址早绑定(编译阶段确定函数地址)

动态多态的函数地址晚绑定(运行阶段确定函数地址)

动态多态的满足条件:

  1. 有继承关系

  1. 子类重写父类的虚函数(不是重载)

动态多态的使用:

父类的指针或引用执行子类对象

虚函数:

在函数名前加上关键字virtual,创建虚函数,会产生一个虚函数(表)指针(vfptr),该指针指向虚函数表内的地址,每一个类有自己的虚函数表,创建虚函数时,该函数的地址就存在虚函数表内。

动态多态解析:

父类创建虚函数后,在父类的虚函数表内存入该虚函数地址,在子类继承父类时,会复制虚函数指针和虚函数表内的地址,在子类重写父类虚函数时,产生的新地址会覆盖子类虚函数表中复制来的父类虚函数地址,所以在父类指针或引用调用子类虚函数时,会到子类虚函数表找函数地址,所以调用的会是子类的虚函数,这就实现了多态。

多态的优点:

  1. 组织结构清晰

  1. 可读性强

  1. 前期和后期拓展和维护性高

2.纯虚函数和抽象类

在多态中父类的虚函数一般实现是没有意义的,都是调用的子类重写的虚函数,所以可以把父类中虚函数改为纯虚函数。

纯虚函数:virtual 返回类型 函数名 (参数列表) = 0;

当类中有了纯虚函数,这个类就叫抽象类

抽象类特点:

·无法实例化对象

·子类必须重写父类中纯虚函数,否则子类也叫抽象类

3.虚析构和纯虚析构

如果子类中有属性开辟到堆区,那父类指针在释放时就不能调用子类的析构代码。

解决方式就是在父类中把析构函数改为虚析构或纯虚析构。

虚析构和纯虚析构的共性:

  1. 可以解决父类指针释放子类对象

  1. 都需要具体函数实现

虚析构和纯虚析构的区别:

如果是纯虚析构,那么这个类就是抽象类。不能实例化对象

虚析构语法:virtual ~类名(){};

虚析构语法:virtual ~类名()=0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值