C++核心高级编程 --- 4.类和对象

news2024/11/24 22:37:23

文章目录

    • 第四章:
      • 4.类和对象
        • 4.1 封装
          • 4.1.1 封装的意义
          • 4.1.2 struct与class的区别
        • 4.2 对象的初始化和清理
          • 4.2.1 构造函数和析构函数
          • 4.2.2 构造函数的分类及调用
          • 4.2.3 拷贝构造函数调用时机
          • 4.2.4 构造函数调用规则
          • 4.2.5 深拷贝与浅拷贝
          • 4.2.6 初始化列表
          • 4.2.7 类对象作为类成员
          • 4.2.8 静态成员
        • 4.3 C++对象模型和this指针
          • 4..3.1 成员变量和成员函数分开存储
          • 4.3.2 this指针
          • 4.3.3 空指针访问成员函数
          • 4.3.4 const修饰成员函数
        • 4.4 友元
          • 4.4.1 全局函数做友元
          • 4.4.2 类做友元
          • 4.4.3 成员函数做友元
        • 4.5 运算符重载
          • 4.5.1 加号运算符重载
          • 4.5.2 左移运算符重载
          • 4.5.3 递增运算符重载
          • 4.5.4 赋值运算符重载
          • 4.5.5 关系运算符重载
          • 4.5.6 函数调用运算符重载
        • 4.6 继承
          • 4.6.1 继承基本语法
          • 4.6.2 继承方式
          • 4.6.3 继承中的对象模型
          • 4.6.4 继承中构造和析构顺序
          • 4.6.5 继承同名成员处理方式
          • 4.6.6 继承同名静态成员处理方式
          • 4.6.7 多继承语法
          • 4.6.8 菱形继承
        • 4.7 多态
          • 4.7.1 多态基本概念
          • 4.7.2 纯虚函数和抽象类
          • 4.7.3 虚析构和纯虚析构
          • 4.7.4 多态案例
            • 案例一:制作计算器类
            • 案例二:制作冷饮
            • 案例三:组装电脑

第四章:

4.类和对象

C++面对对象的三大特征:封装继承多态

4.1 封装
4.1.1 封装的意义
  • 将属性和行为作为一个整体,常用来表现生活中的事物

  • 将属性和行为加以权限以便进行控制

语法结构:class 类名 { 访问权限: 属性 / 行为 };

#include <iostream>
using namespace std;

const int PI = 3.14;

class Circle
{
    //访问权限
    //public - 公共权限
public:

    //属性
    //半径
    int m_r;

    //行为
    //计算圆的面积
    double calculateCircleArea()
    {
        return PI * (m_r * m_r);
    }

};

int main()
{
    //创建一个具体的圆(对象)
    Circle c1;
    //对圆(对象)的属性进行赋值操作
    c1.m_r = 3;

    cout << "半径为" << c1.m_r << "的面积为" << c1.calculateCircleArea() << endl;
    system("pause");
    return 0;
}
#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
    void getID(int id)
    {
        m_id = id;
    }

    void getName(string name)
    {
        m_name = name;
    }

    void getAge(int age)
    {
        m_age = age;
    }

    void showStudent()
    {
        cout << m_name << "学号是" << m_id << "年龄是" << m_age << endl;
    }

public:
    int m_id;
    string m_name;
    int m_age;
};
int main()
{
    Student s1;
    s1.getID(123456);
    s1.getName("小明");
    s1.getAge(18);
    s1.showStudent();
    system("pause");
    return 0;
}

三种访问权限:

  1. public 公共权限

  2. protected 保护权限

  3. private 私有权限

#include <iostream>
using namespace std;

// public    - 类内可访问,类外可访问
// protected - 类内可访问,类外不可访问
// private   - 类内可访问,类外不可访问


class Person
{
public:
    string m_Name;
protected:
    string m_Phone;
private:
    string m_Password;

public:
    void func()
    {
        m_Name = "小红";
        m_Phone = "HW";
        m_Password = "ABC123";
    }
};

int main()
{
    Person p1;
    p1.m_Name = "小明";
    p1.m_Phone = "HW";        //报错,protected类外不可访问
    p1.m_Password = "abc123"; //报错,private类外不可访问
    system("pause");
    return 0;
}
4.1.2 struct与class的区别

默认访问权限不同:

  • struct默认权限为公共

  • class 默认权限为私有

#include <iostream>
#include <string>
using namespace std;

struct Person1
{
    string m_Name;
};

class Person2
{
    string m_Name;
};

int main()
{
    Person1 p1;
    p1.m_Name = "小明";

    Person2 p2;
    p2.m_Name = "小红"; //报错
    system("pause");
    return 0;
}
4.2 对象的初始化和清理
4.2.1 构造函数和析构函数

对象的初始化和清理是两个重要的安全问题:

  1. 一个对象或变量没有初始状态,对于其使用后的结果是未知的。

  2. 使用完一个对象或变量,没及时清理会造成一定的安全问题。

在C++中使用构造函数和析构函数来解决以上问题,如果我们不提供构造和析构函数,编译器会提供构造函数和析构函数(空实现)。

  • 构造函数:作用于创建对象时对对象的成员属性进行赋值,构造函数有编译器自动调用,无需手动调用。

  • 析构函数:作用于在对象销毁前系统自动调用,进行清理工作。

构造函数语法结构:类名(){}

  1. 无返回值,无需写void

  2. 函数名和类型相同

  3. 可以有参数,因此可发生重载

  4. 程序在调用对象时会自动调用,无序手动调用,指挥调用一次

析构函数语法结构:~类名(){}

  1. 无返回值,无需写void

  2. 函数名于类名相同,在名称前加上符号~

  3. 不可有参数,由此不可发生重载

  4. 程序在对象销毁前会自动调用,无序手动调用,只会调用一次。

#include <iostream>
using namespace std;

class Student
{
public:
    Student()
    {
        cout << "Student的构造函数调用" << endl;
    }

    ~Student()
    {
        cout << "Student的析构函数调用" << endl;
    }
};

void test()
{
    Student s;
}

int main()
{
    test();
    system("pause");
    return 0;
}

在这里插入图片描述

4.2.2 构造函数的分类及调用

两种分类方式:

  • 参数分为:有参构造和无参构造

  • 类型分为:普通构造和拷贝构造

三种调用方式:

  • 括号法

  • 显示法

  • 隐式转换法

#include <iostream>
using namespace std;

class Student
{
public:
    Student()
    {
        cout << "Student的默认构造函数调用" << endl;
    }
    Student(int a)
    {
        age = a;
        cout << "Student的有参构造函数调用" << endl;
    }
    Student(const Student& s)
    {
        age = s.age;
        cout << "Student的拷贝构造函数调用" << endl;
    }
    ~Student()
    {
        cout << "Student的析构函数调用" << endl;
    }
    int age;
};

void test()
{
    //1.括号法
    //Student s1();   //调用默认构造函数时,不要加(),编译器会认为是一个函数的声明 
    Student s1;       //默认构造函数调用
    Student s2(18);   //有参构造函数调用
    Student s3(s2);   //拷贝构造函数调用
    cout << "s2的年龄是" << s2.age << endl;
    cout << "s3的年龄是" << s3.age << endl;

    //2.显示法
    Student s4;
    Student s5 = Student(19);
    Student s6 = Student(s5);
    //Student(20);    //匿名对象,当前行执行结束后,系统自动回收掉。
    //Student(s6);      //不要用拷贝构造函数,初始化匿名对象,编译器会认为Student(s5)= Student s5;
    // 
    //3.隐式转换法
    Student s7 = 17;  //相当于Student s7 = Student(17)
    Student s8 = s7;
}
int main()
{
    test();
    system("pause");
    return 0;
}
4.2.3 拷贝构造函数调用时机

三种情况:

  • 使用一个已经创建完毕的对象来初始化一个新的对象

  • 值传递的方式给函数参数传值

  • 以值方式返回局部对象

#include <iostream>
using namespace std;

class Student
{
public:
    Student()
    {
        cout << "Student的默认构造函数调用" << endl;
    }
    Student(int age)
    {
        cout << "Student的有参构造函数调用" << endl;
        m_age = age;
    }
    Student(const Student& s)
    {
        cout << "Student的拷贝构造函数调用" << endl;
        m_age = s.m_age;
    }
    ~Student()
    {
        cout << "Student的析构构造函数调用" << endl;
    }


    int m_age;
};

//使用一个已经创建完毕的对象来初始化一个新的对象
void test01()
{
    Student s1(18);
    Student s2(s1);

    cout << "s2的年龄是" << s2.m_age << endl;
}

void Work1( Student s)
{

}
//值传递的方式给函数参数传值
void test02()
{
    Student s3;
    Work1(s3);
}

Student work2()
{
    Student s5;
    cout << &s5 << endl;
    return s5;
}

//以值方式返回局部对象
void test03()
{
    Student s4 = work2();
    cout << &s4 << endl;
}

int main()
{
    test01();
    test02();
    test03();
    system("pause");
    return 0;
}
4.2.4 构造函数调用规则

默认情况下,C++编译器中至少会给一个类添加三个函数

  • 默认构造函数(无参,函数体为空)

  • 默认拷贝构造函数,对属性进行值拷贝

  • 默认析构函数(无参,函数体为空)

调用规则:

  1. 如果用户定义了有参构造函数,C++将不会提供默认无参构造函数,但会提供默认拷贝构造函数。

  2. 如果用户定义了拷贝构造函数,C++将不会提供其他的构造函数。

//如果用户定义了有参构造函数,C++将不会提供默认无参构造函数,但会提供默认拷贝构造函数。
#include <iostream>
using namespace std;

class Student
{
public:
    Student()
    {
        cout << "Student的默认构造函数调用" << endl;
    }
    Student(int age)
    {
        cout << "Student的有参构造函数调用" << endl;
        m_age = age;
    }
    /*Student(const Student& s)
    {
        cout << "Student的拷贝构造函数调用" << endl;
        m_age = s.m_age;
    }*/
    ~Student()
    {
        cout << "Student的析构函数调用" << endl;
    }

    int m_age;
};

void test01()
{
    Student s1;
    s1.m_age = 18;
    Student s2(s1);
    cout << "s2的年龄是:" << s2.m_age << endl;
}

//void test02()
//{
//    Student s3(20);
//    Student s4(s3);
//    cout << "s4的年龄是" << s4.m_age << endl;
//}

int main()
{
    test01();
    //test02();
    system("pause");
    return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;

class Student
{
public:
    /*Student()
    {
        cout << "Student的默认构造函数调用" << endl;
    }*/
    Student(int age)
    {
        cout << "Student的有参构造函数调用" << endl;
        m_age = age;
    }
    Student(const Student& s)
    {
        cout << "Student的拷贝构造函数调用" << endl;
        m_age = s.m_age;
    }
    ~Student()
    {
        cout << "Student的析构函数调用" << endl;
    }

    int m_age;
};

//void test01()
//{
//    Student s1;
//    s1.m_age = 18;
//    Student s2(s1);
//    cout << "s2的年龄是:" << s2.m_age << endl;
//}

void test02()
{
    Student s3(20);
    Student s4(s3);
    cout << "s4的年龄是" << s4.m_age << endl;
}

int main()
{
    /*test01();*/
    test02();
    system("pause");
    return 0;
}

在这里插入图片描述

4.2.5 深拷贝与浅拷贝

浅拷贝:赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

#include <iostream>
using namespace std;

class Student
{
public:
    Student()
    {
        cout << "Student的默认构造函数调用" << endl;
    }
    Student(int age, int weight)
    {
        m_age = age;
        m_weight = new int(weight);
        cout << "Student的有参构造函数调用" << endl;
    }
    Student(const Student& s)
    {
        cout << "Student的拷贝构造函数调用" << endl;
        m_age = s.m_age;
        m_weight = new int(*s.m_weight);
    }
    ~Student()
    {
        if (m_weight != NULL)
        {
            delete m_weight;
            m_weight = NULL;
        }
        cout << "Student的析构函数调用" << endl;
    }
    int m_age;
    int* m_weight;
};

void test01()
{
    Student s1(18, 120);
    cout << "s1的年龄是" << s1.m_age << ",体重是:" << *s1.m_weight << endl;
    Student s2(s1);
    cout << "s2的年龄是" << s2.m_age << ",体重是:" << *s2.m_weight << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.2.6 初始化列表

作用:初始化属性

语法结构:构造函数():属性1(值1), 属性2(值2)…{}

#include <iostream>
using namespace std;

class Student
{
public:
    Student():m_age(18),m_height(180)
    {

    }
    Student(int age, int height) :m_age(age), m_height(height)
    {

    }
    int m_age;
    int m_height;
};

void test01()
{
    Student s1;
    cout << "s1的年龄是" << s1.m_age << endl;
    cout << "s1的身高是" << s1.m_height << endl;

    Student s2(20,175);
    cout << "s2的年龄是" << s2.m_age << endl;
    cout << "s2的身高是" << s2.m_height << endl;
}

int main()
{    
    test01();
    system("pause");
    return 0;
}

在这里插入图片描述

4.2.7 类对象作为类成员

C++的类中成员可以是另一个类的对象,称该成员为对象成员。

#include <iostream>
#include <string>
using namespace std;

class Phone
{
public:

    Phone(string pName)
    {
        m_PName = pName;
        cout << "Phone的构造函数调用" << endl;
    }

    ~Phone()
    {
        cout << "Phone的析构函数调用" << endl;
    }

    string m_PName;
};

class Student
{
public:
    Student(string name, string pName):m_Name(name), m_Phone(pName)
    {
    /*    m_Name = name;
        m_Phone = pName;*/
        cout << "Student的构造函数调用" << endl;
    }

    ~Student()
    {
        cout << "Student的析构函数调用" << endl;
    }
    string m_Name;
    Phone m_Phone;
};

void test01()
{
    Student s1("小明", "HW");
    cout << s1.m_Name << "使用" << s1.m_Phone.m_PName << "手机" << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

在这里插入图片描述

总结:当其他类对象作为本类成员时,构造时先构造类对象,再构造自身,析构的顺序与构造相反。

4.2.8 静态成员

在成员变量和成员函数前加上关键字static,称为静态成员。

分类

  • 静态成员变量

      所有对象共享同一份数据
    
      在编译阶段时分配内存
    
      类内声明,类外初始化
    
  • 静态成员函数
    所有对象共享同个函数
    静态成员函数只能访问静态成员变量

静态成员变量

#include <iostream>
using namespace std;

class Student
{
public:
    static int m_A;

private:
    static int m_B;
};

int Student::m_A = 10;
int Student::m_B = 20;

void test01()
{
    //静态成员变量两种访问方式

    //通过对象
    Student s1;
    s1.m_A = 100;
    cout << "s1的m_A值为:" << s1.m_A << endl;
    Student s2;
    s2.m_A = 200;
    cout << "s1的m_A值为:" << s1.m_A << endl; //共享同一份数据
    cout << "s2的m_A值为:" << s2.m_A << endl;

    //通过类名
    cout << "s1的m_A值为:" << Student::m_A << endl;
    cout << "s2的m_A值为:" << Student::m_A << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

静态成员函数

#include <iostream>
using namespace std;

class Student
{
public:
    static void func()
    {
        m_A = 10; //静态成员函数可访问静态成员变量
        //m_B = 20; //报错,静态成员函数不可访问非静态成员变量
        cout << "statc void func函数的调用" << endl;
    }
private:
    static void func2()
    {
        m_A = 10;
        //m_B = 20;
        cout << "statc void func2函数的调用" << endl;
    }
public:
    static int m_A;
    int m_B;
};

int Student::m_A = 10;
//int Student::m_B = 20; //报错,非静态成员变量不可在类外定义

void test01()
{
    //通过对象访问
    Student s1;
    s1.func();

    //通过类名访问
    Student::func();

    //Student::func2(); //报错,类外访问不到私有静态成员函数
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.3 C++对象模型和this指针
4…3.1 成员变量和成员函数分开存储

在C++中,只有非静态成员变量才属于类的对象上。

#include <iostream>
using namespace std;

class Student
{
public:
    int m_A;        //非静态成员变量 属于类的对象上

    static int m_B; //静态成员变量 不属于类的对象上

    void func(){}   //非静态成员函数 属于类的对象上

    void func2(){}  //静态成员变量 不属于类的对象上
};

void test01()
{
    Student s1;
    cout << sizeof(s1) << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

补充:空对象占用内存空间为1,C++编译器会给每个空对象也分配一个字节空间,以便区分对象占用内存的位置。

4.3.2 this指针

定义:this指针指向被调用的成员函数所属的对象,隐含每一个非静态成员函数内的一种指针。

作用

  • 当形参和成员变量同名时,可用this指针来进行区分

  • 在类的非静态成员函数中返回对象本身,可用return *this

#include <iostream>
using namespace std;

class Student
{
public:
    Student(int age)
    {
        //当形参和成员变量同名时,可用this指针来进行区分
        this->age = age;
    }
    Student& stuAddAge(Student &s)
    {
        this->age += s.age;
        //在类的非静态成员函数中返回对象本身,可用return *this
        return *this;
    }
    int age;
};

void test01()
{
    Student s1(18);
    cout << "s1的年龄是" << s1.age << endl; //s1的年龄是18
    Student s2(20); 
    s2.stuAddAge(s1);
    cout << "s2的年龄是" << s2.age << endl; //s2的年龄是38

}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.3.3 空指针访问成员函数
#include <iostream>
using namespace std;

class Student
{
public:
    void showClassName()
    {
        cout << "This is Student" << endl;
    }
    void showClassAge()
    {
        if (this == NULL)
            return;
        //如果没有if判断的话会报错,因为传入的指针为NULL
        cout << "m_age = " << this->m_age << endl;
    }
    int m_age;
};

void test01()
{
    Student* s = NULL;
    s->showClassName();
    s->showClassAge();
}

int main()
{
    test01();
    system("pause");
    return 0;
}

补充:空指针是可以调用成员函数的,但要注意是否用到this指针,用到的话需要加以判断来保证代码的健壮性。

4.3.4 const修饰成员函数

常函数

  • 成员函数后加上const后,称该函数为常函数

  • 常函数内不可修改成员的属性

  • 成员属性声明时加上关键字mutable后,在常函数依旧可修改

常对象

  • 声明对象前加上const后,称该对象为常对象

  • 常对象只能调用常函数

#include <iostream>
using namespace std;

class Student
{
public:
    //this指针本质上是一个指针常量,指针的指向是不可修改的
    //在成员函数后加上const,修饰的是this指向,让指针指向的值也不可被修改
    //const Student * const this
    void modifyStudent() const
    {
        //this->m_A = 100; //报错
        this->m_B = 200;
        //this = NULL; //this指针不能修改指针的指向
    }
    void func()
    {
        cout << "This is func" << endl;
    }
    int m_A;
    mutable int m_B;   //特殊变量,即使在常函数中,也可修改
};

void test01()
{
    Student s;
    s.modifyStudent();
}

//常对象
void test02()
{
    const Student s2; //常对象
    //s2.m_A = 100;     //报错
    s2.m_B = 200;
    s2.modifyStudent();
    //s2.func();        //报错,常对象只能调用常函数,不能调用普通成员函数,普通成员函数可以修改属性
}

int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}
4.4 友元

作用:让一个函数或类访问另一个类中私有成员。

关键字:friend

三种实现

  • 全局函数做友元

  • 类做友元

  • 成员函数做友元

4.4.1 全局函数做友元
#include <iostream>
using namespace std;

class House
{
    friend void Visit(House* house);
public:
    House()
    {
        m_LivingRoom = "客厅";
        m_BedRoom = "卧室";
    }
public:
    string m_LivingRoom;
private:
    string m_BedRoom;
};

void Visit(House* house)
{
    cout << "正在参观" << house->m_LivingRoom << endl;
    cout << "正在参观" << house->m_BedRoom << endl;
}

void test01()
{
    House h;
    Visit(&h);
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.4.2 类做友元
#include <iostream>
#include <string>
using namespace std;

class House;
class goodfriend
{
public:
    goodfriend();
    void visit();

private:
    House* house;

};

class House
{
    friend class goodfriend;

public:
    House();

public:
    string m_LivingRoom;
private:
    string m_BedRoom;
};

House::House()
{
    this->m_LivingRoom = "客厅";
    this->m_BedRoom = "卧室";
}

goodfriend::goodfriend()
{
    house = new House;
}

void goodfriend::visit()
{
    cout << "正在参观" << house->m_LivingRoom << endl;
    cout << "正在参观" << house->m_BedRoom << endl;
}

void test01()
{
    goodfriend gf;
    gf.visit();
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.4.3 成员函数做友元
#include <iostream>
#include <string>
using namespace std;

class House;
class goodfriend
{
public:
    goodfriend();
    void visit();
    void visit2();

private:
    House* house;

};

class House
{
    friend void goodfriend::visit();

public:
    House();

public:
    string m_LivingRoom;
private:
    string m_BedRoom;
};

House::House()
{
    this->m_LivingRoom = "客厅";
    this->m_BedRoom = "卧室";
}

goodfriend::goodfriend()
{
    house = new House;
}

void goodfriend::visit()
{
    cout << "正在参观" << house->m_LivingRoom << endl;
    cout << "正在参观" << house->m_BedRoom << endl;
}

void goodfriend::visit2()
{
    cout << "正在参观" << house->m_LivingRoom << endl;
    //cout << "正在参观" << house->m_BedRoom << endl;
}

void test01()
{
    goodfriend gf;
    gf.visit();
    gf.visit2();
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.5 运算符重载

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

4.5.1 加号运算符重载

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

#include <iostream>
using namespace std;

class Student
{
public:
    //成员函数加号运算符重载
    /*Student operator+(Student& s)
    {
        Student temp;
        temp.m_A = this->m_A + s.m_A;
        temp.m_B = this->m_B + s.m_B;
        return temp;
    }*/
    int m_A;
    int m_B;
};

//全局函数加号运算符重载
Student operator+(Student& s1, Student& s2)
{
    Student temp;
    temp.m_A = s1.m_A + s2.m_A;
    temp.m_B = s1.m_B + s2.m_B;
    return temp;
}

//函数重载
Student operator+(Student& s, int val)
{
    Student temp;
    temp.m_A = s.m_A + val;
    temp.m_B = s.m_B + val;
    return temp;
}

void test01()
{
    Student s1;
    s1.m_A = 10;
    s1.m_B = 20;
    Student s2;
    s2.m_A = 10;
    s2.m_B = 20;

    Student s3 = s1 + s2;
    Student s4 = s1 + 10;
    //成员函数本质调用
    //Student s3 = s1.operator+(s2);
    //全局函数本质调用
    //Student s3 = operator+(s1, s3);

    cout << "s3 - m_A = " << s3.m_A << endl;
    cout << "s3 - m_B = " << s3.m_B << endl;
    cout << "s4 - m_A = " << s4.m_A << endl;
    cout << "s4 - m_B = " << s4.m_B << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.5.2 左移运算符重载

作用:可输出自定义数据类型。

#include <iostream>
using namespace std;

class Student
{
    friend ostream& operator<<(ostream& cout, Student& s);
public:
    Student(int a, int b)
    {
        m_A = a;
        m_B = b;
    }

private:
    int m_A;
    int m_B;
};

//使用成员函数重载左移运算符 s1.operator<<(cout) s1 << cout,但无法实现,因为cout在左侧
//只能使用全局函数重载左移运算符
//全局函数重载调用本质 operator<< (cout , p)  cout << p
ostream& operator<<(ostream &cout, Student &s)
{
    cout << "m_A = " << s.m_A << " " << "m_B = " << s.m_B << endl;
    return cout;
}

void test01()
{
    Student s1(10, 20);

    cout << s1 << endl;
    cout << "hello C++" << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.5.3 递增运算符重载

作用:实现自己的整型数据。

#include <iostream>
using namespace std;

class MyIntData                        
{
    friend ostream& operator<<(ostream& cout, MyIntData myInt);
public:
    MyIntData()
    {
        m_Num = 0;
    }

    //前置++
    MyIntData& operator++()
    {
        m_Num++;
        return *this; //将自身返回
    }

    //后置++
    MyIntData operator++(int)
    {
        MyIntData temp = *this; //先记录当前结果
        m_Num++;                //递增
        return temp;            //将记录结果返回
    }
private:
    int m_Num;
};

ostream& operator<<(ostream& cout, MyIntData myInt)
{
    cout << myInt.m_Num;
    return cout;
}

void test01()
{
    MyIntData myInt;
    cout << ++myInt << endl;
    cout << myInt << endl;
}

void test02()
{
    MyIntData myInt2;
    cout << myInt2++ << endl;
    cout << myInt2 << endl;
}

int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}
4.5.4 赋值运算符重载

C++编译器至少给一个类添加了4个函数:

  • 默认构造函数(无参,函数体为空)

  • 默认析构函数(无参,函数体为空)

  • 默认拷贝构造函数,对属性进行值拷贝

  • 赋值运算符 operator=,对属性进行值拷贝

类中有属性指向堆区,赋值操作时也会出现深浅拷贝问题。

#include <iostream>
using namespace std;

class Student
{
public:
    Student(int age)
    {
        m_Age = new int(age);
    }
    ~Student()
    {
        if (m_Age != NULL)
        {
            delete m_Age;
            m_Age = NULL;
        }
    }
    Student& operator=(Student& s)
    {
        if (m_Age != NULL)
        {
            delete m_Age;
            m_Age = NULL;
        }
        m_Age = new int(*s.m_Age);
        return *this;
    }
    int *m_Age;
};

void test01()
{
    Student s1(18);
    Student s2(20);
    Student s3(19);
    s1 = s2 = s3;
    cout << "s1的年龄是:" << *s1.m_Age << endl;
    cout << "s2的年龄是:" << *s2.m_Age << endl;
    cout << "s3的年龄是:" << *s2.m_Age << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.5.5 关系运算符重载

作用:可让两个自定义类型对象进行比较操作。

#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
    Student(string name, int age)
    {
        m_Name = name;
        m_Age = age;
    }

    bool operator==(Student& s)
    {
        if (this->m_Age == s.m_Age && this->m_Name == s.m_Name)
            return true;
        return false;
    }
    bool operator!=(Student& s)
    {
        if (this->m_Age == s.m_Age && this->m_Name == s.m_Name)
            return false;
        return true;
    }

    string m_Name;
    int m_Age;
};

void test01()
{
    Student s1("小明", 18);
    Student s2("小红", 18);
    if (s1 == s2)
    {
        cout << "s1 = s2" << endl;
    }
    else
    {
        cout << "s1 != s2" << endl;
    }
    if (s1 != s2)
    {
        cout << "s1 != s2" << endl;
    }
    else
    {
        cout << "s1 = s2" << endl;
    }
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.5.6 函数调用运算符重载
  • 函数调用运算符()也可重载

  • 重载后使用的方式极其像函数的调用方式,因此称为仿函数

  • 仿函数无固定写法,比较灵活

#include <iostream>
using namespace std;

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

void Printf02(string test)
{
    cout << test << endl;
}

class numAdd
{
public:
    int operator()(int value1, int value2)
    {
        return value1 + value2;
    }
};

void test01()
{
    Printf p;
    p("Hello C++");  //仿函数
    Printf02("Hello C++");
}

void test02()
{
    numAdd a;
    int result = a(10, 20);
    cout << "result = " << result << endl;
    cout << " numAdd()(20, 20) = " << numAdd()(20, 20) << endl; //匿名对象调用

}

int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}
4.6 继承

继承是面向对象三大特性之一。

类与类存在着特殊的关系

在这里插入图片描述

下级成员不仅有上一级的共性,还会有自己的特性,利用继承技术,可减少重复代码。

4.6.1 继承基本语法

语法结构:class 子类 : 继承方式 父类

#include <iostream>
using namespace std;

class BasePage
{
public:
    void top()
    {
        cout << "欢迎来到abc线上购物中心" << endl;
    }
    void left()
    {
        cout << "Shoes、Water、Paper、Clothes...(公共分类列表)" << endl;
    }
    void bottom()
    {
        cout << "服务中心、购物指导、联系我们...(公共底部)" << endl;
    }
};

class Shoes :public BasePage
{
public:
    void conter()
    {
        cout << "You can buy shoes" << endl;
    }
};

class Water :public BasePage
{
public:
    void conter()
    {
        cout << "You can buy water" << endl;
    }
};

class Paper :public BasePage
{
public:
    void conter()
    {
        cout << "You can buy paper" << endl;
    }
};

class Clothes :public BasePage
{
public:
    void conter()
    {
        cout << "You can buy clothes" << endl;
    }
};

void test01()
{
    cout << "鞋子购买页面如下:" << endl;
    Shoes sh;
    sh.top();
    sh.bottom();
    sh.left();
    sh.conter();
    cout << "--------------------" << endl;
    cout << "矿泉水购买页面如下:" << endl;
    Water wt;
    wt.top();
    wt.bottom();
    wt.left();
    wt.conter();
    cout << "--------------------" << endl;
    cout << "纸巾购买页面如下:" << endl;
    Paper pr;
    pr.top();
    pr.bottom();
    pr.left();
    pr.conter();
    cout << "--------------------" << endl;
    cout << "衣服购买页面如下:" << endl;
    Clothes cl;
    cl.top();
    cl.bottom();
    cl.left();
    cl.conter();
}

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

补充

  • 子类也称派生类

  • 父类也称基类

派生类成员含两大部分

  1. 从积累继承过来的

  2. 自己增加的成员

基类继承来的成员体现其共性,新增成员体现其个性。

4.6.2 继承方式

继承语法结构:class 子类 : 继承方式 父类

三种继承方式

  • 公共继承

  • 保护继承

  • 私有继承

在这里插入图片描述

#include <iostream>
using namespace std;

class Father
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son1 :public Father
{
public:
    void func()
    {
        m_A = 10; //父类中的公共权限成员,到子类依然是公共权限
        m_B = 20; //父类中的保护权限成员,到子类依然是保护权限
        //m_C = 30; //报错,父类中的私有权限成员,子类没权限访问
    }
};

class Son2 :protected Father
{
public:
    void func()
    {
        m_A = 10;    //父类中的公共权限成员,到子类变保护权限
        m_B = 20;    //父类中的保护权限成员,到子类依然是保护权限
        //m_C = 30;  //报错,父类中的私有权限成员,子类没权限访问
    }
};

class Son3 :private Father
{
public:
    void func()
    {
        m_A = 10;      //父类中的公共权限成员,到子类变私有权限
        m_B = 20;      //父类中的保护权限成员,到子类变私有权限
        //m_C = 30;      报错,父类中的私有权限成员,子类没权限访问
    }
};

class GrandSon :public Son3
{
public:
    void func()
    {
        //m_A = 10;      //报错,m_A变为私有权限,访问不到
        //m_B = 20;      //报错,m_B变为私有权限,访问不到
    }
};

void test01()
{
    Son1 s1;
    s1.m_A = 10;
    //s1.m_B = 20;  //报错,m_B是保护权限,类外访问不到

    Son2 s2;
    //s2.m_A = 10;   //报错,m_A是保护权限,类外访问不到
    //s2.m_B = 20;   //报错,m_B是保护权限,类外访问不到

    Son3 s3;
    //s3.m_A = 10;   //报错,m_A是私有权限,类外访问不到
    //s3.m_B = 20;     //报错,m_B是私有权限,类外访问不到
}

int main()
{
    test01();
    system("pause");
    return 0;
}#include <iostream>
using namespace std;

class Father
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son1 :public Father
{
public:
    void func()
    {
        m_A = 10; //父类中的公共权限成员,到子类依然是公共权限
        m_B = 20; //父类中的保护权限成员,到子类依然是保护权限
        //m_C = 30; //报错,父类中的私有权限成员,子类没权限访问
    }
};

class Son2 :protected Father
{
public:
    void func()
    {
        m_A = 10;    //父类中的公共权限成员,到子类变保护权限
        m_B = 20;    //父类中的保护权限成员,到子类依然是保护权限
        //m_C = 30;  //报错,父类中的私有权限成员,子类没权限访问
    }
};

class Son3 :private Father
{
public:
    void func()
    {
        m_A = 10;      //父类中的公共权限成员,到子类变私有权限
        m_B = 20;      //父类中的保护权限成员,到子类变私有权限
        //m_C = 30;      报错,父类中的私有权限成员,子类没权限访问
    }
};

class GrandSon :public Son3
{
public:
    void func()
    {
        //m_A = 10;      //报错,m_A变为私有权限,访问不到
        //m_B = 20;      //报错,m_B变为私有权限,访问不到
    }
};

void test01()
{
    Son1 s1;
    s1.m_A = 10;
    //s1.m_B = 20;  //报错,m_B是保护权限,类外访问不到

    Son2 s2;
    //s2.m_A = 10;   //报错,m_A是保护权限,类外访问不到
    //s2.m_B = 20;   //报错,m_B是保护权限,类外访问不到

    Son3 s3;
    //s3.m_A = 10;   //报错,m_A是私有权限,类外访问不到
    //s3.m_B = 20;     //报错,m_B是私有权限,类外访问不到
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.6.3 继承中的对象模型

从父类继承过来的成员,哪些是属于子类对象中呢?

#include <iostream>
using namespace std;

class Father
{
public:
    int a;
protected:
    int b;
private:
    int c;
};

class Son : public Father
{
public:
    int d;
};

void test01()
{
    //父类中的所有非静态成员属性都被被子类所继承下去
    //父类中的私有成员属性也会被继承下去,只是被编译器隐藏了,因此访问不到
    cout << "sizeof(Son)的结果为" << sizeof(Son) << endl; //16
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.6.4 继承中构造和析构顺序

子类在继承父类后,创建子类对象,也会调用父类的构造函数。

#include <iostream>
using namespace std;

class Father
{
public:
    Father()
    {
        cout << "Father的构造函数调用" << endl;
    }
    ~Father()
    {
        cout << "Father的析构函数调用" << endl;
    }
};

class Son : public Father
{
public:
    Son()
    {
        cout << "Son的构造函数调用" << endl;
    }
    ~Son()
    {
        cout << "Son的析构函数调用" << endl;
    }
};

void test01()
{
    Son s1;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

在这里插入图片描述

总结:先构造父类,再构造子类,析构的顺序跟构造相反。

4.6.5 继承同名成员处理方式

子类与父类中有同名的成员,怎样通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可

  • 访问父类同名成员 加作用域即可

#include <iostream>
using namespace std;

class Father
{
public:
    Father()
    {
        m_A = 10;
    }
    void func()
    {
        cout << "Father - func调用" << endl;
    }
    void func(int )
    {
        cout << "Father - func(int)调用" << endl;
    }
    int m_A;
};

class Son :public Father
{
public:
    Son()
    {
        m_A = 20;
    }
    void func()
    {
        cout << "Son - func调用" << endl;
    }
    int m_A;
};

void test01()
{
    Son s1;
    cout << "s1 m_A = " << s1.m_A << endl;
    cout << "Father m_A = " << s1.Father::m_A << endl;

    Son s2;
    s2.func();
    s2.Father::func();
    //s2.func(30)  //报错
    //子类中如果出现与父类同名的成员函数,子类会将父类中的所有同名成员函数隐藏
}

int main()
{
    test01();
    system("pause");
    return 0;
}

总结:子类对象可直接访问子类中同名成员,子类对象加上作用域可访问到父类同名成员,当子类与父类有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可访问到父类中同名函数。

4.6.6 继承同名静态成员处理方式

继承中同名的静态成员怎样在子类对象上进行访问呢?

静态成员与非静态成员出现同名的情况,处理方式一样

  • 访问子类同名成员 直接访问即可

  • 访问父类同名成员 加作用域即可

#include <iostream>
using namespace std;

class Base
{
public:
    static int m_A;
    static void func()
    {
        cout << "Base - static void func调用" << endl;
    }
    static void func(int)
    {
        cout << "Son - static void func(int)调用" << endl;
    }
};
int Base::m_A = 10; //类外初始化

class Son :public Base
{
public:
    static int m_A;
    static void func()
    {
        cout << "Son - static void func调用" << endl;
    }
};
int Son::m_A = 20;

//同名静态成员属性
void test01()
{
    //通过对象访问
    Son s1;
    cout << "s1 m_A = "  << s1.m_A << endl;
    cout << "Base m_A = " << s1.Base::m_A << endl;

    //通过类名访问
    cout << "s1 m_A = " << Son::m_A << endl;
    cout << "Base m_A = " << Son::Base::m_A << endl;
}


//同名静态成员函数
void test02()
{
    //通过对象访问
    Son s2;
    s2.func();
    s2.Base::func();

    //通过类名访问
    Son::func();
    Son::Base::func();
    Son::Base::func(10);

}

int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}

总结:非静态成员有两种访问方式,对象访问和类名访问。

4.6.7 多继承语法

在C++中允许一个类继承一个类

语法结构:class 子类 : 继承方式 父类1, 继承方式 父类2…

#include <iostream>
using namespace std;

class Base1
{
public:
    Base1()
    {
        m_A = 10;
    }
    int m_A;
};

class Base2
{
public:
    Base2()
    {
        m_A = 20;
    }
    int m_A;
};

class Son :public Base1, public Base2
{
public:
    Son()
    {
        m_B = 30;
        m_C = 40;
    }
    int m_B;
    int m_C;
};

void test01()
{
    Son s1;
    cout << "sizeof(Son) = " << sizeof(Son) << endl;
    //父类中出现同名成员,需加作用域区分
    cout << "Base1 m_A = " << s1.Base1::m_A << endl;
    cout << "Base2 m_A = " << s1.Base2::m_A << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

在这里插入图片描述

4.6.8 菱形继承

概念

  • 两个派生类继承同一个基类

  • 又有某个类同时继承这两个派生类

在这里插入图片描述

1.羊继承了动物的数据,驼也继承了动物的数据,羊驼使用数据时,会产生二义性。

2.羊驼继承动物的数据两份,其实该数据只需要一份即可。

#include <iostream>
using namespace std;

class Animal
{
public :
    int m_Age;
};

class Sheep :virtual public  Animal //加上关键字virtual变成虚继承
{

};
//Animal类称为虚基类
class Camel :virtual public Animal  //加上关键字virtual变成虚继承
{

};

class SheepCamel :public Sheep, public Camel
{

};

void test01()
{
    SheepCamel sc;
    sc.Sheep::m_Age = 18;
    sc.Camel::m_Age = 18;
    //菱形继承,两个父类拥有相同数据,需加作用域区分
    cout << "sc.Sheep::m_Age = " << sc.Sheep::m_Age << endl;
    cout << "sc.Camel::m_Age = " << sc.Camel::m_Age << endl;
    cout << "sc.m_Age = " << sc.m_Age << endl;
    //菱形继承导致数据有两份,资源浪费,可使用虚继承来解决该问题 
}

int main()
{
    test01();
    system("pause");
    return 0;
}

在这里插入图片描述

4.7 多态

多态的优点:

  • 代码组织结构清晰

  • 可读性强

  • 利于前后期的扩展及维护

多态使用条件:父类指针或引用指向子类对象。

4.7.1 多态基本概念

多态是C++面向对象三大特征之一

多态分两类

  • 静态多态:函数重载与运算符重载属于静态多态,复用函数名

  • 动态多态:派生类与虚函数实现运行时多态

静态多态与动态多态区别:

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

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

//静态多态的函数地址早绑定 - 编译阶段确定函数地址
#include <iostream>
using namespace std;

class Afternoon 
{
public:
    void Drink()
    {
        cout << "Water" << endl;
    }
};

class Person1 :public Afternoon
{
public:
    void Drink()
    {
        cout << "Coffee" << endl;
    }
};

class Person2 : public Afternoon
{
public:
    void Drink()
    {
        cout << "Tea" << endl;
    }
};

void doWork(Afternoon &af) 
{
    af.Drink();
}

void test01()
{
    Person1 p1;
    doWork(p1); //结果:Water
}

int main()
{
    test01();
    system("pause");
    return 0;
}
//动态多态的函数地址晚绑定 - 运行阶段确定函数地址
#include <iostream>
using namespace std;

class Afternoon
{
public:
    virtual void Drink() //虚函数
    {
        cout << "Water" << endl;
    }
};

class Person1 :public Afternoon
{
public:
    void Drink()
    {
        cout << "Coffee" << endl;
    }
};

class Person2 : public Afternoon
{
public:
    void Drink()
    {
        cout << "Tea" << endl;
    }
};

//如果想要喝咖啡,需要让函数地址不提前绑定,在运行时绑定,地址晚绑定
void doWork(Afternoon& af)
{
    af.Drink();
}

void test01()
{
    Person1 p1;
    doWork(p1); //结果:Coffee

    Person2 p2;
    doWork(p2); //结果:Tea
}

int main()
{
    test01();
    system("pause");
    return 0;
}

补充:

动态多态满足条件:

  1. 继承关系

  2. 子类重写父类的虚函数

重写:函数返回值类型 函数名 参数列表完全一致。

4.7.2 纯虚函数和抽象类

父类中虚函数实现是无意义的,主要用来调用子类重写内容,我们可将虚函数改成纯虚函数。

语法结构:virtual 返回值类型 函数名 (参数列表) = 0 ;

当某个类中有了纯虚函数,那么也称该类为抽象类。

抽象类特点

  • 无法实例化对象

  • 子类必须重写抽象类中纯虚函数,否则也属于抽象类

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void func() = 0; //纯虚函数
};


class Son : public Base      //抽象类的子类必须重写父类中的纯虚函数,不然也属于抽象类
{
public:
    virtual void func()
    {
        cout << "Son - func调用" << endl;
    }
};

void test01()
{
    //Base b1;   //报错,抽象类无法实例化对象
    //new Base;  //报错,抽象类无法实例化对象
    //Son s1;    //类必须重写父类中的纯虚函数,否则无法实例化对象
    Base* bs = new Son;
    bs->func();  //结果:Son - func调用
}

int main()
{
    test01();
    system("pause");
    return 0;
}
4.7.3 虚析构和纯虚析构

多态使用时,如子类有属性开辟到堆区上,那父类指针在释放时无法调用子类的析构代码
将父类的析构函数改为虚析构或者纯虚析构即可解决以上问题
虚析构和纯虚析构的共性

  • 可解决父类指针释放子类对象问题

  • 都要有具体函数的实现

虚析构和纯虚析构的区别

  • 如果有纯虚析构,该类属于抽象类,无法实例化对象

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

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

                               类名::类名(){}
#include <iostream>
#include <string>
using namespace std;

class Eat
{
public:
    Eat()
    {
        cout << "Eat 构造函数调用" << endl;
    }
    virtual void Drinking() = 0;

    //virtual ~Eat() //加上virtual,变成虚析构函数
    //{
    //    cout << "Drinking 虚析构函数调用" << endl;
    //}

    virtual ~Eat() = 0;
};

Eat::~Eat() //纯虚析构,该类属于抽象类,无法实例化对象
{
    cout << "Eat 纯虚析构函数调用" << endl;
}

class Coffee :public Eat
{
public:
    Coffee(string feeding)
    {
        cout << "Coffee 构造函数调用" << endl;
        m_Feeding = new string(feeding);
    }

    virtual void Drinking()
    {
        cout << *m_Feeding << "加入咖啡" << endl;
    }

    ~Coffee()
    {
        cout << "Coffee 析构函数调用" << endl;
        if (this->m_Feeding != NULL)
        {
            delete m_Feeding;
            m_Feeding = NULL;
        }
    }
public:
    string *m_Feeding;
};

void test01()
{
    Eat* eat = new Coffee("牛奶");
    eat->Drinking();

    //通过父类指针释放,导致子类对象清理不干净,容易造成内存泄漏
    //给基类加一个虚析构函数即可解决父类指针释放子类对象问题
    delete eat;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

4.7.4 多态案例
案例一:制作计算器类

说明:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

普通写法

#include <iostream>
#include <string>
using namespace std;

class calculator
{
public:
    int operation(string oper)
    {
        if (oper == "+")
        {
            return m_Num1 + m_Num2;
        }
        else if (oper == "-")
        {
            return m_Num1 - m_Num2;
        }
        else if (oper == "*")
        {
            return m_Num1 * m_Num2;
        }
        else if (oper == "/")
        {
            return m_Num1 / m_Num2;
        }
        else
        {
            return 0;
        }
    }
    int m_Num1;
    int m_Num2;
};

void test01()
{
    calculator calc;
    calc.m_Num1 = 10;
    calc.m_Num2 = 20;
    cout << calc.m_Num1 << " + " << calc.m_Num2 << " = " << calc.operation("+") << endl;
    cout << calc.m_Num1 << " - " << calc.m_Num2 << " = " << calc.operation("-") << endl;
    cout << calc.m_Num1 << " * " << calc.m_Num2 << " = " << calc.operation("*") << endl;
    cout << calc.m_Num1 << " / " << calc.m_Num2 << " = " << calc.operation("/") << endl;

}

int main()
{
    test01();
    system("pause");
    return 0;
}

多态技术

#include <iostream>
using namespace std;

class AbstractCalculator //定义一个计算器抽象类
{
public:
    virtual int operation()
    {
        return 0;
    }
    int m_Num1;
    int m_Num2;
};

class AddCalculator :public AbstractCalculator  //加法
{
    int operation()
    {
        return m_Num1 + m_Num2;
    }
};

class SubCalculator :public AbstractCalculator  //减法
{
    int operation()
    {
        return m_Num1 - m_Num2;
    }
};

class MulCalculator :public AbstractCalculator   //乘法
{
    int operation()
    {
        return m_Num1 * m_Num2;
    }
};

class divCalculator :public AbstractCalculator   //除法
{ 
    int operation()
    {
        return m_Num1 / m_Num2;
    }
};

void test01()
{
    //加法
    AbstractCalculator* calc = new AddCalculator;
    calc->m_Num1 = 10;
    calc ->m_Num2 = 20;
    cout << calc->m_Num1 << " + " << calc->m_Num2 << " = " << calc->operation() << endl; //30
    delete calc; //用完销毁

    //减法
    calc = new SubCalculator;
    calc->m_Num1 = 10;
    calc->m_Num2 = 20;
    cout << calc->m_Num1 << " - " << calc->m_Num2 << " = " << calc->operation() << endl; //-10
    delete calc; //用完销毁

    //乘法
    calc = new MulCalculator;
    calc->m_Num1 = 10;
    calc->m_Num2 = 20;
    cout << calc->m_Num1 << " * " << calc->m_Num2 << " = " << calc->operation() << endl; //200
    delete calc; //用完销毁

    //除法
    calc = new divCalculator;
    calc->m_Num1 = 10;
    calc->m_Num2 = 20;
    cout << calc->m_Num1 << " / " << calc->m_Num2 << " = " << calc->operation() << endl; //0
    delete calc; //用完销毁

}

int main()
{
    test01();
    system("pause");
    return 0;
}

案例二:制作冷饮

大致流程:煮水 - 冲泡 - 倒杯 - 加料 - 加冰

利用多态技术实现,提供抽象制作冷饮基类,提供子类制作拿铁和柠檬红茶

#include <iostream>
using namespace std;

class AbstractDrinking
{
public:
    virtual void Boil() = 0;      //煮水

    virtual void Brew() = 0;      //冲泡

    virtual void PoutGlass() = 0; //倒杯

    virtual void Feeding() = 0;   //加料

    virtual void AddIce() = 0;    //加冰

    void MakeDrink()              //制作冷饮
    {
        Boil();
        Brew();
        PoutGlass();
        Feeding();
        AddIce();
    }
};

class Latte :public AbstractDrinking //拿铁
{
public:
    virtual void Boil() //煮水
    {
        cout << "煮矿泉水" << endl;
    }

    virtual void Brew() //冲泡
    {
        cout << "冲泡咖啡粉" << endl;
    }

    virtual void PoutGlass() //倒杯
    {
        cout << "倒入玻璃杯中" << endl;
    }

    virtual void Feeding() //加料
    {
        cout << "加入牛奶" << endl;
    }

    virtual void AddIce()  //加冰
    {
        cout << "加入冰块" << endl;
    }
};

class LemonBlackTea :public AbstractDrinking //柠檬红茶
{
public:
    virtual void Boil() //煮水
    {
        cout << "煮矿泉水" << endl;
    }

    virtual void Brew() //冲泡
    {
        cout << "冲泡红茶" << endl;
    }

    virtual void PoutGlass() //倒杯
    {
        cout << "倒入玻璃杯中" << endl;
    }

    virtual void Feeding() //加料
    {
        cout << "加入柠檬片" << endl;
    }
    virtual void AddIce()  //加冰
    {
        cout << "加入冰块" << endl;
    }
};

void doWork(AbstractDrinking* abd) //制作函数
{
    abd->MakeDrink();
    delete abd;
}

void test01()
{
    doWork(new LemonBlackTea);
    cout << "-----------" << endl;
    doWork(new Latte);
}

int main()
{
    test01();
    system("pause");
    return 0;
}
案例三:组装电脑

说明:电脑主要组成部件、CPU、内存条、显示器。

  • 将每个零件封装出抽象类,并提供不同厂商生产不同的零件,如Intel厂商和Nvidia厂商

  • 创建电脑类提供让电脑工作的函数,并调用每个零件工作接口

  • 测试时组装三台不同的电脑进行工作

#include <iostream>
using namespace std;

class CPU
{
public:
    virtual void calculate() = 0;
};

class Memory
{
public:
    virtual void storage() = 0;
};

class Display
{
public:
    virtual void display() = 0;
};

class Computer
{
public:
    Computer(CPU* cpu, Memory* mem, Display* dp)
    {
        m_cpu = cpu;
        m_mem = mem;
        m_dp = dp;
    }

    void doWork() //工作函数
    {
        //调用接口
        m_cpu->calculate();
        m_mem->storage();
        m_dp->display();
    }

    ~Computer()
    {
        if (m_cpu != NULL) //释放CPU部件
        {
            delete m_cpu;
            m_cpu = NULL;
        }

        if (m_dp != NULL)  //释放显示器部件
        {
            delete m_cpu;
            m_dp = NULL;
        }

        if (m_mem != NULL)  //释放内存条部件
        {
            delete m_cpu;
            m_mem = NULL;
        }
    }
private:
    CPU* m_cpu;    
    Memory* m_mem;
    Display* m_dp;

};

class IntelCPU :public CPU
{
public:
    virtual void calculate()
    {
        cout << "Intel - CPU 开始运行工作" << endl;
    }
};

class NvidiaCPU :public CPU
{
public:
    virtual void calculate()
    {
        cout << "Nvidia - CPU 开始运行工作" << endl;
    }
};

class IntelDisplay :public Display
{
public:
    virtual void display()
    {
        cout << "Intel - 显示器 开始显示工作" << endl;
    }
};

class NvidiaDisplay :public Display
{
public:
    virtual void display()
    {
        cout << "Nvidia - 显示器 开始显示工作" << endl;
    }
};

class IntelMemory :public Memory
{
public:
    virtual void storage()
    {
        cout << "Intel - 内存条 开始存储工作" << endl;
    }
};

class NvidiaMemory :public Memory
{
public:
    virtual void storage()
    {
        cout << "Nvidia - 内存条 开始存储工作" << endl;
    }
};

void test01()
{
    //第一台电脑零部件
    /*CPU* intelCpu = new IntelCPU;
    Display* intelDisplay = new IntelDisplay;
    Memory* intelMem = new IntelMemory;*/
    //创建第一台电脑
    cout << "第一台电脑开始工作" << endl;
    Computer* computer1 = new Computer(new IntelCPU, new IntelMemory, new IntelDisplay);  //第一台电脑
    computer1->doWork();
    delete computer1;
    cout << "----------------------" << endl;
    cout << "第二台电脑开始工作" << endl;
    Computer* computer2 = new Computer(new NvidiaCPU,new NvidiaMemory, new NvidiaDisplay);//第二胎电脑
    computer2->doWork();
    delete computer2;
    cout << "----------------------" << endl;
    cout << "第三台电脑开始工作" << endl;
    Computer* computer3 = new Computer(new IntelCPU, new NvidiaMemory, new IntelDisplay); //第三台电脑
    computer3->doWork();
    delete computer3;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1565897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实现BOA蝴蝶优化算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝴蝶优化算法(butterfly optimization algorithm, BOA)是Arora 等人于2019年提出的一种元启发式智能算…

使用kubeadm工具搭建Kubernetes集群

本文目录 一、CentOS7最小化安装&#xff08;master&#xff09;1、下载ISO镜像2、安装3、进入centos安装界面4、安装最小化安装必要的一些工具 二、克隆虚拟机&#xff08;node1、node2&#xff09;三、基础配置1、节点规划——部署架构图2、防火墙和SElinux配置2、主机名和ho…

nginx与tomcat的区别?

关于nginx和tomcat的概念 网上有很多关于nginx和tomcat是什么东西的定义&#xff0c;我总结了一下: tomcat是Web服务器、HTTP服务器、应用服务器、Servlet容器、web容器。 Nginx是Web服务器、HTTP服务器、正向/反向代理服务器&#xff0c;。 这里有两个概念是交叉的&#xff…

科技团队治理能力成长路线图

点击&#x1f446;蓝字 关注我们 本文观点&#xff5c;吴穹 主笔&#xff5c;AI小助手 温馨提示&#xff1a;干货长文&#xff0c;建议收藏阅读喔&#xff5e; 引言 2024年3月20日&#xff0c;吴穹博士于上海交通大学上海高级金融学院同一众信托行业金融科技管理者进行了《金融…

软件架构风格_2.调用/返回体系结构风格

调用/返回风格是指在系统中采用了调用与返回机制。利用调用-返回实际上是一种分而治之的策略&#xff0c;其主要思想是将一个复杂的大系统分解为若干子系统&#xff0c;以便降低复杂度&#xff0c;并且增加可修改性。程序从其执行起点开始执行该构件的代码&#xff0c;程序执行…

海外媒体软文发稿:带动海外宣发新潮流,迈向国际舞台

引言 随着全球化的发展&#xff0c;越来越多的中国企业希望在国际舞台上展示自己的实力。而海外媒体软文发稿作为一种全新的海外宣传方式&#xff0c;正逐渐成为带动海外宣发新潮流的有力工具。本文将探讨海外媒体软文发稿的优势和如何迈向国际舞台。 海外媒体软文发稿的优势…

tcpdump + wireshark 服务器抓包分析

tcpdump wireshark 服务器抓包分析 1.tcpdump安装2.tcpdump使用3.安装wireshark4.使用wireshark 本文用以总结使用tcpdump进行抓包&#xff0c;然后使用wireshark工具打开抓包出来的pacp文件进行分析。通过tcpdump可以实时监控到linux服务器中tcp和http、https等通讯的内容和信…

LVGL:拓展部件——日历 lv_calendar

一、概述 此控件特点&#xff1a; 以7x7矩阵的形式展示任何一个月的日期&#xff0c;即在一个7行7列的网格中呈现。显示星期的名称&#xff0c;即每一列对应一个特定的星期几&#xff08;如周一、周二等&#xff09;。高亮显示当前日期&#xff08;即今天&#xff09;。支持高…

强大缓存清理工具 NetShred X for Mac激活版

NetShred X for Mac是一款专为Mac用户设计的强大缓存清理工具&#xff0c;旨在帮助用户轻松管理和优化系统性能。这款软件拥有直观易用的界面&#xff0c;即使是初次使用的用户也能快速上手。 软件下载&#xff1a;NetShred X for Mac激活版下载 NetShred X能够深入扫描Mac系统…

深入理解MySQL:拼接字符串、查询、删除表和创建索引的关键命令

MySQL是一种功能强大的关系型数据库管理系统&#xff0c;广泛应用于各种类型的应用程序中。本文将介绍MySQL中一些常用的关键命令&#xff0c;包括拼接字符串、查询、删除表和创建索引&#xff0c;帮助读者更好地理解和利用MySQL数据库。 mysql拼接字符串 在MySQL中&#xf…

[RK3588-Android12] 调试MIPI-双通道-压缩屏(Video Mode/MIPI Dphy 8Lane/DSC 144HZ)

问题描述 被测屏幕&#xff1a;小米Pad6 分辨率&#xff1a;1800X2880 模式&#xff1a;Video Mode/MIPI Dphy 8Lane/DSC 144HZ PPS: 11 00 00 89 30 80 0B 40 03 84 00 14 01 C2 01 C2 02 00 01 F4 00 20 01 AB 00 06 00 0D 05 7A 06 1A 18 00 10 F0 03 0C 20 00 06 0B 0B 33…

谈谈考研数学几个常见误区

25考研数学&#xff0c;一定一定要吃透基础&#xff0c;练好计算 我之所以要强调这个&#xff0c;是因为现在的考研数学&#xff0c;越来越重视基础和计算的考察&#xff0c;题海战术已经过时&#xff0c;如果想要有效的提升自己&#xff0c;要进行针对性的学习。我去年考研的…

【云呐】固定资产清查盘点报告模板

固定资产清查盘点报告的大致框架:一、前言说明本次清查盘点的背景和目的清晰表述清查盘点的责任与相关依据二、清查盘点范围按部门或区域明确清查范围口径明确被清查项目,如所有原值一定数额以上的固定资产三、清查盘点时间确定清查盘点实施的时间节点 四、清查盘点方法描述清查…

01 Python进阶:正则表达式

re.match函数 使用 Python 中的 re 模块时&#xff0c;可以通过 re.match() 函数来尝试从字符串的开头匹配一个模式。以下是一个简单的详解和举例&#xff1a; import re# 定义一个正则表达式模式 pattern r^[a-z] # 匹配开头的小写字母序列# 要匹配的字符串 text "h…

采用大语言模型进行查询重写——Query Rewriting via Large Language Models

文章&#xff1a;Query Rewriting via Large Language Models&#xff0c;https://arxiv.org/abs/2403.09060 摘要 查询重写是在将查询传递给查询优化器之前处理编写不良的查询的最有效技术之一。 手动重写不可扩展&#xff0c;因为它容易出错并且需要深厚的专业知识。 类似地…

异常-模块-包-Python

师从黑马程序员 异常的概念 异常演示 捕获异常的方法 try:fopen("D:/abc.txt","r",encoding"UTF-8") except:print("出现异常了&#xff0c;因为文件不存在&#xff0c;我将open的模式&#xff0c;改为w模式去打开")fopen("D:/a…

继阿里EMO后,腾讯也提出了AniPortrait:用于生成由音频和参考肖像图像驱动的高质量动画

腾讯提出了一种新颖的框架-AniPortrait&#xff0c;用于生成由音频和参考肖像图像驱动的高质量动画。通俗讲&#xff0c;就是给张照片生成说话的视频。类似阿里的EMO&#xff0c;大家先可以简单看下效果。 相关链接 论文&#xff1a;arxiv.org/abs/2403.17694 代码&#xff1a…

【技术访谈】与Babbage项目团队畅谈BSV区块链的新代码库

​​发表时间&#xff1a;2024年3月12日 BSV区块链协会近期宣布上线了JavaScript和TypeScript SDK&#xff0c;后者旨在为开发者提供先进的统一核心代码库&#xff0c;以便利开发者在BSV区块链上开发具备可扩展性的应用程序。 新上线的SDK替代了此前被广泛使用的、但已长期不再…

家用洗地机如何选?四款2024高性能洗地机力荐

家庭清洁是每个家庭都需要面对的日常任务&#xff0c;而一款高效、实用的洗地机能够极大地减轻我们的清洁负担。然而&#xff0c;在众多洗地机品牌和型号中选择一款适合自己的产品并不容易。本文将为您提供一份全面的洗地机选购指南&#xff0c;帮助您了解各种洗地机的特点和功…

el-form表单去除部分校验结果

需求说明 如图&#xff1a; 当点击右边的叉时&#xff0c; 需要删除这个输入框&#xff0c;删除这个输入框只需要一个变量就可以解决。 但使用v-show的方式去除这个输入框的时候&#xff0c;但下面的校验信息却没有消失 解决方法 找了半天&#xff0c;表单局部重置的方法&a…