C++基础运算符重载和继承

目录

学习内容:

1.运算符重载

1.1 运算符种类

1.2 运算符重载函数

1.3 调用原则和调用机制

1.4 运算符重载函数的格式

1.5 算术运算符

1.6 赋值类运算符重载

1.7 关系运算符重载函数

1.8 单目运算符

1.9 自增自减运算

1.10 插入和提取运算符重载

1.11 类型转换运算符

1.12 函数对象(仿函数)

1.13 运算符重载的限制

2. 静态成员 

2.1 静态成员变量

2.2 静态成员函数 

2.3 C/C++中static的总结(面试题) 

 3. 继承

3.1 继承的作用

3.2 继承的格式

3.3 继承方式 

3.4 继承过程中的成员


学习内容:

1.运算符重载

1.1 运算符种类

        单、算、移、关、逻辑、条件表达式、赋值、逗号

1.2 运算符重载函数

        1> 函数名:统一为 operator# //这里的#表示要被重载的运算符

        2> 参数:根据运算符的功能和位置而定,全局函数版双目运算符参数有两个,成员函数版双目运算符参数一个

                全局函数版,单目运算符参数有一个,成员函数版单目运算符参数无

        3> 返回值:根据运算符的特征而定

1.3 调用原则和调用机制

        调用时机:当对象使用该运算符时,系统自动调用运算符重载函数

        调用机制:左调右参原则,运算符左侧是函数的调用者,运算符右侧是函数的参数

                例如:a = b; //a.operator=(b);

                                s1 + s2; //s1.operator+(s2);

1.4 运算符重载函数的格式

        1> 任意一个运算符重载都有两个版本:成员函数版和全局函数版

        2> 实际使用过程中,只实现一种重载的情况,常用成员函数版

1.5 算术运算符

        1> 种类:+、-、*、/、%

        2> 算术运算符属于双目运算符,格式:L # R; L表示左操作数,#表示运算符号,R表示右操作数

        3> 左操作(L):既可以是左值也可以是右值

             右操作数(R):既可以是左值也可以是右值

              运算结果:只能是右值

        4> 格式:

                成员函数版:const 类名 operator#(const 类名 &R) const

                解析:第一个const保护运算结果不被修改

                            第二个const保护右操作数在运算过程中不被修改

                            第三个const保护左操作数自身在运算过程中不被修改

                全局函数版:const 类名 operator#(const 类名 &L,const 类名 &R)

1.6 赋值类运算符重载

        1> 种类:=、+=、-=、*=、/=、%=

        2> 赋值运算符属于双目运算符,格式:L # R; L表示左操作数,#表示运算符号,R表示右操作数

        3>    左操作(L):只能是左值

                右操作数(R):既可以是左值也可以是右值

                运算结果:左操作数自身的引用

        4> 格式:

                成员函数版:类名 & operator#(const 类名 &R)

                全局函数版:类名 & operator#( 类名 &L,const 类名 &R)

1.7 关系运算符重载函数

        1> 种类:>、=、

        2> 关系运算符属于双目运算符,格式:L # R; L表示左操作数,#表示运算符号,R表示右操作数

        3>    左操作(L):既可以是左值也可以是右值

                右操作数(R):既可以是左值也可以是右值

                运算结果:bool类型的右值

        4> 格式:

                成员函数版:const bool operator#(const 类名 &R) const

                全局函数版:const bool operator#(const 类名 &L,const 类名 &R)

1.8 单目运算符

        1> 种类:-(负号) 、!

        2> 格式:# R; #表示运算符号,R表示右操作数

        3> 操作数(R):既可以是左值也可以是右值

                运算结果:同类的一个右值

        4> 格式:

                成员函数版:const 类名 operator#() const

                全局函数版:const bool operator#(const 类名 &R)

1.9 自增自减运算

        1> 种类:++ 、 --

        2> 格式:# R; #表示运算符号,R表示右操作数

        3> 操作数(R):只能是左值

                运算结果:

        前置:是自身的引用

        成员格式:类名 &operator#()

        全局格式:类名 &operator#(类名 &R)

        后置:临时值

        成员格式:类名 operator#(int)

        全局格式:类名 operator#(类名 &R, int)

1.10 插入和提取运算符重载

        1> 插入和提取运算符的来源        

namespace std {

        ostream cout;

        istream cin;

}

        2> 由于运算符重载函数的调用遵循左调有参原则,所以,自定义类的重载函数,需要流对象和自定义类的对象

        例如:cout<

        3> 此时,原则上来说,需要在流所在的类对象中,写成员函数,难度较大,所以,对于该类的重载函数,我们只能实现全局函数版,将该函数作为类的友元函数   

        4> 格式:

                ostream& operator

                istream& operator>>(istream &L, 类名 &R)

1.11 类型转换运算符

        1> 有时,需要将类对象进行强制类型转换为其他数据类型,此时就需要在类体内提供类型转换运算符重载函数

        2> 定义格式: operator 要转换的类型() { 返回对应类型的数据 }

1.12 函数对象(仿函数)

        1> 本质上就是重载 () 运算符,由于调用时,跟函数调用一样,所以称为仿函数

        2> 定义格式: operator() (参数列表) {函数体内容}

        3> 调用格式:对象名(实参名); //对象 . operator() (实参名)

1.13 运算符重载的限制

1> 运算符重载只能在已有的运算符基础上进行重载,不能凭空捏造一个运算符

2> 运算符重载不能更改运算符的本质:如不能在加法运算符重载中实现减法

3> 运算符重载不能改变运算符的优先级

4> 运算符重载不能改变运算符的结合律

5> 运算符重载函数不能设置默认参数

6> 成员函数版的参数要比全局函数版的参数少一个,原因是对象本身就是一个参数

#include 


using namespace std;


//定义一个复数类: 由实部和虚部组成的一个数据  a + bi
class Complax
{
private:
    int real;            //实部
    int vir;             //虚部


public:
    Complax() {cout<<"无参构造"< 0)
        {
            cout<real + R.real;
        temp.vir = this->vir + R.vir;


        return temp;
    }


    //实现全局函数版,需要将全局函数版设置成友元函数
    friend const Complax operator-(const Complax &L, const Complax &R);


    //成员函数版完成加等于运算符重载: 实部 += 实部   虚部+=虚部
    Complax &operator+=(const Complax &R)
    {
        this->real += R.real;
        this->vir += R.vir;


        return *this;
    }


    //定义成员函数版完成关系运算符重载: 实部>实部   && 虚部>虚部
     bool operator>(const Complax &R)const
    {
        return this->real>R.real && this->vir>R.vir;
    }


     //定义成员函数版完成负号运算符重载:   实部 = -实部    虚部=-虚部
     const Complax operator-()const
     {
         Complax temp;
         temp.real = -this->real;
         temp.vir = -this->vir;


         return temp;
     }


     //定义成员函数版完成++前置运算: 实部 +=1   虚部+=1
     Complax &operator++()
     {
         this->real++;
         this->vir++;


         return *this;
     }


     //定义成员函数版完成++后置运算: 实部 +=1   虚部+=1
     Complax operator++(int)        //这里括号中的int仅仅起到占位作用,使用的是哑元
     {
         Complax temp;
         temp.real = this->real++;
         temp.vir = this->vir++;


         return temp;
     }


     //将插入运算符重载函数,设置成友元函数
     friend ostream & operator<<(ostream &L, const Complax &R);


     //定义类型转换运算符重载函数
     operator int()
     {
         return this->real + this->vir;
     }


     //重载小括号运算符,完成仿函数
     void operator()(string msg)
     {
         cout << msg<< endl;
     }




};


//定义全局函数版完成两个复数相减
const Complax operator-(const Complax &L, const Complax &R)
{
    Complax temp;       //无参构造
    temp.real = L.real - R.real;
    temp.vir = R.vir - R.vir;


    return temp;
}


//自定义插入运算符重载函数:只能实现全局函数版
ostream & operator<<(ostream &L, const Complax &R)
{
    //输出内容
    if(R.vir > 0)
    {
        L<c2)                      //调用关系运算符重载函数
    {
        cout<<"yes"<

2. 静态成员 

        所谓静态成员,就是将类中的某个或某几个成员,依赖于类对象而独立存

        在程序编译阶段,就给静态成员分配空间,即使没有类对象,静态成员的空间依然已经分配

        相当于在类体内,定义一个生命周期比较常的数据

        静态成员分配,静态成员变量和静态成员函数

2.1 静态成员变量

        1> 定义格式:在定义成员变量时,在前面加关键字static,该成员变量就是静态成员变量

        2> 静态成员的权限一般为public,必须在全局处进行定义,类内只是声明,如果没有在全局处进行定义,那么默认为0

        3> 静态成员变量,不占用类对象的内存空间,在编译时系统就分配了内存空间

        4> 虽然静态成员不占用类对象的空间,但是,也可以通过类对象调用静态成员变量

        5> 静态成员变量的访问:有两种访问形式

                对象名.静态成员变量

                类名::静态成员变量

        6> 所有类对象,共享该静态成员变量,无论实例化多少个类对象,静态成员只有一份

        7> 静态成员变量,从功能上来说,可以理解成是一个全局变量,但是相比于全局变量而言,静态成员变量更加体现了类的封装性

#include 


using namespace std;


class Stu
{
private:
    int age;


public:
    static int flag;           //类内声明


public:
    Stu() {}
};


//类外定义静态成员变量
int Stu::flag = 520;




int main()
{
    cout<2.2 静态成员函数  
  

        1> 定义格式:在定义成员函数前加关键字static,那么该函数就是静态成员函数

        2> 和静态成员变量一样,不依附于某个具体的类对象,属于整个类的

        3> 静态成员函数的调用方式也有两种:通过类对象调用,通过类名直接调用

        4> 静态成员函数中,只能使用静态成员变量,不能使用非静态成员变量

        5> 静态成员变量中没有this指针,但是,同名的静态成员函数与非静态成员函数不构成重载关系

#include 


using namespace std;


class Stu
{
private:
    int age = 18;


public:
    static int flag;           //类内声明


public:
    Stu() {}


    //非静态成员函数
    void show()
    {
        cout<<"age = "<age<<"     falg = "<flag<

2.3 C/C++中static的总结(面试题) 

        1> 修饰函数的局部变量,表示将当前变量独立于整个函数而存在,其生命周期随着整个程序的编译而产生,整个程序结束而终止,即使函数没有被调用,静态成员变量的内存空间也已经存在了,即使函数以及运行结束,静态成员变量的生命周期也没有结束。一次初始化,后期调用函数时,都具有保值功能。

        2> 修饰类中成员变量,为静态成员变量,不依附于类对象而存在,编译时系统分配空间,需要在全局处进行定义。即使没有类对象,也可以通过类名直接使用,也可以通过类对象进行使用。多个对象,共享静态成员变量,即使类对象的空间全部都释放后,静态成员的空间也没有结束,而是随着整个程序的结束而结束。

        3> 修饰全局变量时,表示限制作用域为当前文件,只能在当前文件内使用该全局变量,其他文件不能使用,即使使用extern也不可以

        4> 修饰全局函数时,表示限制作用域为当前文件,只能在当前文件内使用该函数,其他文件中不可以使用,即使使用了联合编译

        5> 修饰类中的成员函数,表示静态成员函数,独立于类体而存在,也可以使用类对象进行调用。即使没有实例化对象,也可以类名调用,静态成员函数中,只能访问静态成员变量,不能访问非静态成员变量。没有this指针。

 3. 继承

        面向对象三大特征:封装、继承、多态

        继承:所谓继承,就是基于一个已有的类,来创建出一个新类的过程叫做继承。主要提高代码的复用性。

3.1 继承的作用

        1> 实现代码的复用性

        2> 继承是实现多态的重要基础,没有继承就没有多态

3.2 继承的格式

class 子类名 :继承方式  class 父类名1, class 父类2。。。
{
    子类扩展的成员;
};

3.3 继承方式 

        1> 继承方式一共有三种:public、protected、private

        2> 回顾访问权限

                public:能在类内、子类以及类外被访问

                protected:能在类内、子类中被访问,类外不允许访问

                private:只能在类内被访问,子类、类外不允许被访问

        3>继承方式时父类中的所有访问权限在子类中的表现形式

父类:            public|protected|private|不能访问        public|protected|private|不能访问        public|protected|private|不能访问
继承方式                    public                                    protected                                private
子类:            public|protected|不能访问|不能访问        protected|protected|不能访问|不能访问    private|private|不能访问|不能访问
1、继承方式其实就是子类继承父类中访问权限的最高权限
2、默认继承方式是私有继承,常用的继承方式是public

        4> 访问权限也可以省略不写,如果省略不写,则默认的继承方式为私有的(private)

        5> 常用的继承方式是:public

#include 


using namespace std;


//定义父类
class Person
{
public:
    string name = "张三";        //公共成员姓名
protected:
    int pwd = 666666;            //受保护成员 银行卡密码
private:
    int money = 888888;           //私有成员 私房钱


public:
    Person() {}
    Person(string n, int p, int m):name(n),pwd(p),money(m) {}
    ~Person() {}


    void show()
    {
        cout<<"name = "<3.4 继承过程中的成员 
  

        1> 子类会继承父类中的所有成员,包括私有成员,只不过不能子类中不能使用父类的私有成员。如果非要使用父类的私有成员,需要在父类中提供public或者protected类型的接口函数完成对私有成员的操作。

        2> 想要完成对 子类从父类中继承下来的成员的初始化工作,需要在子类的初始化列表中,显性调用父类的构造函数来完成。如果没有显性调用父类的有参构造,那么系统会自动调用父类的无参构造,来完成对继承下来的成员的初始化工作。

        在这个过程中,虽然调用的父类的构造函数,但是并没有实例化父类对象,最终对象的个数只有一个

        3> 类与类之间的关系模型

                1、继承关系:is a 模型,是特殊的包含关系(has a模型)

                2、包含关系:has a模型,在一个类中,有另一个类的成员子对象

                3、友元关系:use a模型,在一个类中,使用另一类中的内容

#include 


using namespace std;


//定义父类
class Person
{
public:
    string name = "张三";        //公共成员姓名
protected:
    int pwd = 666666;            //受保护成员 银行卡密码
private:
    int money = 888888;           //私有成员 私房钱


public:
    Person() {cout<<"Person::无参构造"<

你可能感兴趣的:(c++,开发语言)