• C++学习笔记——02


    一、结构体

    结构体是一 群数据类型的集合。它也是一种数据类型。

    (一)声明结构体

    结构体的名字与类一样,首字母大写。

    注意:声明结构体时必须使用struct 修饰,调用、创建结构体变量时可以省略。

    1. 方式一:
    #include 
    
    using namespace std;
    
    struct Person {
        int id = 0; //在声明结构体时可以给结构体中的数据,一个初始值。
        string name = "lihua";
        int age = 18;
        char sex = 'm';
    };
    
    int main() {
        Person person1;
        Person person2;
        person1.name = "lihua";
        person2.name = "xiaoming";
        cout << person1.name << endl;
        cout << person2.name << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 方式二:
    #include 
    
    using namespace std;
    
    //使用关键字 typedef  给结构体一个别名
    typedef  struct Person {
        int id = 0;
        string name = "lihua";
        int age = 18;
        char sex = 'm';
    } Student;
    
    int main() {
        Student student1;
        Student student2;
        student1.name = "lihua";
        student2.name = "xiaoming";
        cout << student1.name << endl;
        cout << student2.name << endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (二)使用结构体

    1. 方式一:
    #include 
    
    using namespace std;
    
    typedef struct Person {
        int id = 0;
        string name = "lihua";
        int age = 18;
        char sex = 'm';
    } Student;
    
    int main() {
        Student student;
        student.id = 123;
        student.name = "lihua";
        student.age = 18;
        student.age = 'w';
        cout << student.id << endl;
        cout << student.name << endl;
        cout << student.age << endl;
        cout << student.sex << endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 方式二:
    #include 
    
    using namespace std;
    
    typedef struct Person {
        int id = 0;
        string name = "lihua";
        int age = 18;
        char sex = 'm';
    } Student;
    
    int main() {
    	//创建并初始化。
        Student student = {123,"lihua",18,'w'};
        cout << student.id << endl;
        cout << student.name << endl;
        cout << student.age << endl;
        cout << student.sex << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (三)结构体数组

    1. 方式一:
    #include 
    
    using namespace std;
    
    typedef struct Person {
        int id = 0;
        string name = "lihua";
        int age = 18;
        char sex = 'm';
    } Student;
    
    void printf(const Student &student) {
        cout << student.id << endl;
        cout << student.name << endl;
        cout << student.age << endl;
        cout << student.sex << endl;
    }
    
    int main() {
        Student students[3] = {
                {1, "a", 18, 'w'},
                {2, "b", 19, 'm'},
                {3, "c", 20, 'm'}
        };
        for (int i = 0; i < size(students); ++i) {
            printf(students[i]);
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    1. 方式二:
    #include 
    
    using namespace std;
    
    typedef struct Person {
        int id = 0;
        string name = "lihua";
        int age = 18;
        char sex = 'm';
    } Student;
    
    void printf(const Student &student) {
        cout << "id = "<<student.id << " "<< "name = "<<student.name << " "<< "age = "<<student.age << " "<< "sex = "<<student.sex << endl;
    }
    
    void printf(Student *student) {
        cout << "id = "<<student->id << " "<< "name = "<<student->name << " "<< "age = "<<student->age << " "<< "sex = "<<student->sex << endl;
    }
    
    int main() {
        auto *s = new Student[3]; //等价于Student *s = new Student[3];
        for (int i = 0; i < 3; ++i) {
            printf(s);
            s++;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    二、内存模型

    C++程序在执行时,将内存大方向划分为4个区域

    • 代码区:存放函数体的二进制代码,由操作系统进行管理的。注意:代码区的局部变量,不要返回它的指针和引用
    • 全局区:存放全局变量和静态变量以及常量。
    • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
    • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

    C++中在程序运行前分为全局区和代码区

    • 代码区特点是共享和只读
    • 全局区中存放全局变量、静态变量、常量
    • 常量区中存放 const修饰的全局常量 和 字符串常量

    (一)new 关键字的使用

    栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
    由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。在C++中主要利用new在堆区开辟内存

    new关键字可以在堆区中开辟内存。开辟后的内存程序不会自动释放,当程序结束运行后,操作系统才会回收。

    #include 
    #include 
    
    using namespace std;
    
    
    string *useNew() {
        //使用new关键字开辟数据类型为int的内存空间,并初始化值为10;
        int *ip = new int(3);
        cout << *ip << endl;
    
        //使用new开辟一个float类型的内存空间。
        float *fp  = new float;
        //初始化内存空间存储的值
        *fp = 9.9;
        cout << *fp << endl;
    
        //使用new开辟大小为3的int数组
        int *array = new int[*ip]{1, 2, 3};
        //遍历
        for (int j = 0; j < 3; ++j) {
            cout << *array << endl;
            array++;
        }
        //创建字符穿变量
        string *str = new string("hello world");
        cout << *str << endl;
    
    
    
        //返回使用new创建的字符的地址。
        return str;
    }
    
    int main() {
        string *strP = useNew();
        cout << *strP << endl;
        cout << *strP << endl;
        cout << *strP << endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在这里插入图片描述

    (二)delete 关键字的使用

    delete 关键字可以释放有new开辟的内存空间。
    释放多个地址,比如:数组 。使用delete [ ] p;

    #include 
    #include 
    
    using namespace std;
    
    
    string *useNew() {
        //使用new关键字开辟数据类型为int的内存空间,并初始化值为10;
        int *ip = new int(3);
        cout << *ip << endl;
    
        //使用new开辟一个float类型的内存空间。
        float *fp = new float;
        //初始化内存空间存储的值
        *fp = 9.9;
        cout << *fp << endl;
    
        //使用new开辟大小为3的int数组
        int *array = new int[*ip]{1, 2, 3};
        //遍历
        for (int j = 0; j < 3; ++j) {
            cout << *array << endl;
            array++;
        }
        //创建字符穿变量
        string *str = new string("hello world");
        cout << *str << endl;
    
    
        //返回使用new创建的字符的地址。
        return str;
    }
    
    //使用delete关键字,主动释放new开辟的内存空间
    void useDelete(string *strP) {
        //通过指针,指定释放那个地址的内存空间
        delete strP;
    }
    
    int main() {
        string *strP = useNew();
        cout << *strP << endl;
        cout << *strP << endl;
        cout << *strP << endl;
        cout << "地址:" << strP << endl;
        useDelete(strP);
        //再次调用会报错
        cout << *strP << endl;
    
    
    	//释放数组
        int *arrayP = new int[3]{1,2,4};
        delete [] arrayP;
        
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    注释掉cout << *strP << endl;的情况下运行:

    在这里插入图片描述

    释放内存后继续调用该地址的指针。报错

    在这里插入图片描述

    (三)如何返回一个局部变量的地址

    由C++的内存可以知道,局部变量在函数调用结束后会自动释放内存。因此,不要直接在函数中返回局部变量的地址。

    #include 
    #include 
    
    using namespace std;
    
    int *test() {
        int a = 10;
        return &a;
    }
    
    int main() {
    
        int *a = test();
        cout << *a << endl;
        cout << *a << endl;
        cout << *a << endl;
        cout << *a << endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    输出:
    在这里插入图片描述
    除了第一个打印的值外,其他的都是乱码。原因是,函数执行完后,会释放局部变量开辟的内存。至于第一个还能访问成功是c++为了防止用户意外调用,保留了内存的一次访问,之后就释放了内存。

    想要返回一个局部变量的地址,必须使用new 关键字开辟空间,然后将这个变量返回。这样在函数调用结束后,才不会被回收内存。

    int *test() {
        //错误方式
        //int a = 10;
        //return &a;
        
        //正确方式
        int *a = new int(10);
        return a;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三、引用的使用

    1. 引用可以简化指针的使用。
    2. 引用必须初始化,且初始化后,不能改变(指向的内存空间不会改变)。
    3. 引用的本质在c++内部实现是一个指针常量。发现是引用,转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改指向

    语法: 数据类型 &别名 = 原名

    //引用的使用
    #include 
    
    using namespace std;
    
    void test() {
        //引用必须初始化,且初始化后,不能改变。
        //int &d1 ;
        int test1 = 1;
        int test2 = 2;
        int &d2 = test1;
        d2 = test2; // 注意:这是赋值操作,不是改变引用的指向
        //通过d2引用赋值操作,test1的内容已经发生改变。
        cout << test1 << endl;
        cout << d2 << endl;
    }
    
    int main() {
        int a = 10;
        //声明变量时前面用&符号表示:引用(引用直接指向a的地址,修改d,a也会变)
        int &d = a;
        //声明变量时前面用*符号表示:指针(&a表示取a的地址。)
        int *p = &a;
        cout << &a << endl;
        cout << &d << endl;
        cout << p << endl;
        //*p表示获取p指向的内存地址存放的值。
        //cout << *p << endl;
        d = 100;
        cout << a << endl;
    
        test();
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    注意:引用与指针一样,不能返回局部引用。

    //引用的使用
    #include 
    
    using namespace std;
    
    int &errorTest() {
        int a = 10;
        return a;
    }
    
    int &correctTest() {
        //静态变量,存放在全局区
        static int a = 100;
        return a;
    }
    
    int main() {
        int &test1 = errorTest();
        int &test2 = correctTest();
    
        //调用一次后,局部变量内存空间地址被释放
        cout << test1 << endl;
        cout << test1 << endl;
        cout << test1 << endl;
    
        //由于静态变量存放在全局区,所以地址在程序结束运行后才释放,不影响调用。
        cout << test2 << endl;
        cout << test2 << endl;
        cout << test2 << endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述

    常量引用 const int& ref = 10; 等价于 const int* const ref = &a; 常量指针常量。它的指向和指向的内容都是不可变的。

    四、类

    C++面向对象的三大特性为:封装、继承、多态。
    封装性与java一致,有三种:public 公共权限、protected 保护权限、private 私有权限。

    (一)创建类

    创建类,一般在头文件中声明类的属性(变量),方法(函数)。
    在源文件中实现类。具体如下:
    注意:类名首字母大写。

    1. 头文件:Person.h
    //
    // Created by wuyuj on 2022/8/26.
    //
    #include 
    using namespace std;
    
    #ifndef LEARN_01_PERSON_H
    #define LEARN_01_PERSON_H
    
    
    class Person {
    private:
        int id;
        string name;
        int age;
        char sex;
    
    public:
        //无参构造函数
        Person();
        //有参构造函数
        Person(int id, const string &name, int age, char sex);
        //析构函数
        virtual ~Person();
    
        //说出自己的名字
        void say();
    
        int getId() const;
    
        void setId(int id);
    
        const string &getName() const;
    
        void setName(const string &name);
    
        int getAge() const;
    
        void setAge(int age);
    
        char getSex() const;
    
        void setSex(char sex);
    };
    
    
    #endif //LEARN_01_PERSON_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    1. 源文件:Person.cpp
    //
    // Created by wuyuj on 2022/8/26.
    //
    #include "Person.h"
    
    void Person::say() {
        cout << "我的名字是:" << name << endl;
    }
    
    Person::Person() {
        cout << "调用无参构造函数,创建对象。"  << endl;
    }
    
    Person::Person(int id, const string &name, int age, char sex) : id(id), name(name), age(age), sex(sex) {
        cout << "调用有参构造函数,创建对象。"  << endl;
    }
    
    Person::~Person() {
        cout << "调用析构函数,对象已经销毁。" <<"析构的对象的id是:"<<this->id << endl;
    }
    
    int Person::getId() const {
        return id;
    }
    
    void Person::setId(int id) {
        Person::id = id;
    }
    
    const string &Person::getName() const {
        return name;
    }
    
    void Person::setName(const string &name) {
        Person::name = name;
    }
    
    int Person::getAge() const {
        return age;
    }
    
    void Person::setAge(int age) {
        Person::age = age;
    }
    
    char Person::getSex() const {
        return sex;
    }
    
    void Person::setSex(char sex) {
        Person::sex = sex;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    (二)创建对象

    1. 方式一:像基本类型一样创建对象。类名 变量名; (默认调用无参构造器)
    2. 方式二:显示的调用构造器创建。
    3. 方式三:通过new关键字创建。
    #include 
    #include "Person.h"
    
    using namespace std;
    
    int main() {
        //通过new 创建。可以选择构造器。
        auto *person = new Person();
        person->setName("李华");
        person->setId(1);
        cout << "名字为:" << person->getName() << endl;
        //通过new 创建的对象,用完需要销毁,否则造成内存泄漏。如果不销毁等程序运行结束,操作系统才会回收。
        delete person;
    
    
        //下面创建的对象,在函数运行结束后,直接销毁。
        //直接创建,默认无参构造函数。
        Person person1;
        person1.setId(2);
    
        //指定构造函数创建。
        Person person2 = Person();
        person2.setId(3);
        Person person3 = Person(4, "李华", 18, 'm');
        cout << "我的名字是:" << person3.getName() << ",今年:" << person3.getAge() << "岁。" << endl;
    
        //匿名对象,没有名字的对象,只能使用一次。用完直接析构
        Person(5, "小明", 18, 'm').say();
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果:

    在这里插入图片描述

    (三)销毁对象

    局部(函数中)创建的对象,在函数结束运行后,自动调用析构函数就行销毁。

    全局中的对象需要使用关键字delete 进行 销毁。比如:使用new关键字创建的对象。

    (四)class与struct的区别

    在C++中 struct和class区别就在于 默认的访问权限不同
    区别:

    • struct 默认权限为公共
    • class 默认权限为私有

    还有就是class类可以有函数。

    在这里插入图片描述

    (五)构造函数

    构造函数两种分类方式:

    1. ​ 按参数分为: 有参构造和无参构造

    ​ 2. 按类型分为: 普通构造和拷贝构造

    默认情况下,c++编译器至少给一个类添加3个函数:

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

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

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

    构造函数默认规则如下:

    如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造。

    如果用户定义拷贝构造函数,c++不会再提供其他构造函数。

    1. 单参数构造函数,可以隐式调用。一般在构造函数前面 使用explicit 修饰,避免隐式调用。
      下面是隐式调用的例子:
    //
    // Created by wuyuj on 2022/8/30.
    //
    #include 
    
    using namespace std;
    
    
    class Test {
        int i = 0;
    public:
        int p = 0;
        string str = "hello";
    
        Test(int p) : p(p) {}
    
        Test(const string &str) : str(str) {}
    };
    
    int main() {
    
        //会隐式调用参数类型匹配的单参构造器。
        Test test = 100;
        cout << test.p << endl;
        
        string name = "lihua";
        Test test1 = name;
        cout << test.str << endl;
        
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    1. 自己创建有参构造函数后,c++编辑器不再提供无参的。

    在这里插入图片描述

    1. 默认的拷贝构造函数(浅拷贝)
    //
    // Created by wuyuj on 2022/8/30.
    //
    #include 
    
    using namespace std;
    
    
    class Test {
        int i = 0;
    public:
        int p = 0;
        string str = "hello";
    
        Test(int p) : p(p) {}
    
        Test(const string &str) : str(str) {}
    };
    
    int main() {
    
        //会隐式调用参数类型匹配的单参构造器。
        Test test = 100;
        test.str = "拷贝";
        cout << test.p << endl;
    
        //使用默认提供的拷贝构造函数,会将test对象的属性的值赋值给test2.
        Test test2 = Test(test);
        cout << test2.p << test2.str << endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述

    1. 浅拷贝和深拷贝
    • 浅拷贝
      demo1:
    //
    // Created by wuyuj on 2022/8/30.
    //
    #include 
    
    using namespace std;
    
    
    class Test {
        int i = 0;
    public:
        int a = 0;
        string str = "hello";
        int *p = nullptr;
    
        Test() {}
    
        Test(int a) : a(a) {}
    
        Test(const string &str) : str(str) {}
    
    };
    
    void copyTest() {
        Test test1;
        test1.a = 10;
        test1.str = "test1";
        test1.p = &test1.a;
    
        //进行浅拷贝后,test1.p和test2.p的指向了同一块内存,也就是 test1对象的属性a 。
        // 当通过test2.p改变属性a的值后,test1对象的属性a也会受到影响。
        //也就是没有完全拷贝。
        Test test2 = Test(test1);//浅拷贝
    
        *test2.p = 200;
    
        cout << "发现对象test1的a属性也发生了改变:" << *test1.p << endl;
    }
    
    int main() {
    	//浅拷贝
        copyTest();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    demo2:

    //
    // Created by wuyuj on 2022/8/30.
    //
    #include 
    
    using namespace std;
    
    class Teacher {
    public:
        int id = 0;
        string name = "xiaohong";
        int age = 18;
    
        Teacher() {}
    
        Teacher(int id, const string &name, int age) : id(id), name(name), age(age) {}
    };
    
    class Student {
    public:
        int id = 0;
        string name = "lihua";
        int age = 18;
        Teacher *teacher = nullptr;
    };
    
    void copyTest() {
        Student student1;
        student1.id = 1;
        student1.name = "小明";
        student1.age = 18;
        Teacher teacher =Teacher(2, "李老师", 45);
        student1.teacher = &teacher;
    
        //拷贝
        Student student2 = Student(student1);
        student2.teacher->name = "黄老师";
        
        
        //同样发现student1的老师也被改变了。
        cout << student1.teacher->name << endl;
    }
    
    int main() {
        //浅拷贝
        copyTest();
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 深拷贝

    想要实现深拷贝很简单,只需要我们自定义拷贝函数即可。

    //
    // Created by wuyuj on 2022/8/30.
    //
    #include 
    
    using namespace std;
    
    class Teacher {
    public:
        int id = 0;
        string name = "xiaohong";
        int age = 18;
    
        Teacher() {}
    
        Teacher(int id, const string &name, int age) : id(id), name(name), age(age) {}
    
    };
    
    class Student {
    public:
        int id = 0;
        string name = "lihua";
        int age = 18;
        Teacher *teacher = nullptr;
    
        Student() {}
    
        //自定义拷贝构造函数
        Student(const Student &student) {
            id = student.id;
            name = student.name;
            age = student.age;
            //对于是指针类型的属性,需要重新分配内存。让指针指向一块新的内存空间。
            teacher = new Teacher(student.teacher->id, student.teacher->name, student.teacher->age);
        }
    
        //注意:因为拷贝的对象的teacher属性是通过new创建的,因此需要重写析构函数,释放内存。
        ~Student() {
            //注意:这里释放内存会报错,原因,创建student1的teacher时不是new出来的,因此不能正常释放内存。暂时想不到方法解决
            //delete teacher;
        }
    };
    
    void copyTest() {
        Student student1;
        student1.id = 1;
        student1.name = "小明";
        student1.age = 18;
        Teacher teacher = Teacher(2, "李老师", 45);
        student1.teacher = &teacher;
    
        //拷贝
        Student student2 = Student(student1);
        student2.teacher->name = "黄老师";
    
    
        //student1的老师没有被改变。
        cout << student1.teacher->name << endl;
    }
    
    int main() {
        //深拷贝
        copyTest();
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    (六)关键字friend(友元)

    friend 关键字与java里面的反射有点相似的地方,那就是能访问封装类型为私有的属性和方法。
    在类中声明哪些类、方法能访问自己的私有属性和方法。

    一共有三种方式:

    1. 全局函数做友元:全局函数中能调用该类的私有属性和方法。
    2. 类做友元:类中能调用其他类的私有属性和方法。
    3. 成员函数做友元:成员函数中能调用其他类的私有属性和方法。
    //friend关键字的使用
    #include 
    
    using namespace std;
    
    class Student;
    class Teacher;
    
    
    class Student {
        //方式一:告诉编译器 global全局函数 是 Building类的好朋友,可以访问类中的私有内容
        friend void global(Student & student);
    
        //方式二:通过friend修饰其他类,那么在这个类中就能调用私有变量和方法。
        friend class Teacher;
    
    
    private:
        int money = 100;
        void myMoney() const {
            cout << "我有这么多钱:" << money << endl;
        }
    
    public:
        int id;
        string name;
        int age;
    };
    
    class Teacher{
    private:
        int id;
        string name;
        int age;
    public:
        void getStudent(){
            Student student;
            //方式二:通过friend修饰其他类,那么在这个类中就能调用它的私有变量和方法。
            student.myMoney();
        }
    };
    
    
    
    //方式一:通过friend 修饰全局函数,调用私有变量和方法。
    void global(Student & student){
        cout << student.money<< endl;
        student.myMoney();
    }
    
    
    
    class Test2{
    public:
        void getTest1();
    };
    
    class Test1{
        //方式三:通过friend修饰其他类的成员方法,那么在这个方法中就能调用私有变量和方法。
        friend void Test2::getTest1();
    private:
        int a = 10;
    };
    
    //方式三:通过friend修饰其他类的成员方法,那么在这个方法中就能调用私有变量和方法。
    void Test2::getTest1(){
        Test1 test1;
        cout <<"方式三:"<<test1.a <<endl;
    }
    
    
    
    int main() {
        Student student;
        //方式一:通过friend 修饰全局函数,调用私有变量和方法。
        global(student);
    
        //方式二:在一个类中通过friend,修饰其他类,那么在这个类中就能调用私有变量和方法。
        Teacher teacher;
        teacher.getStudent();
    
        //方式三:通过friend修饰其他类的成员方法,那么在这个方法中就能调用私有变量和方法。
        Test2 test2;
        test2.getTest1();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    (七)对象关系运算符重载 operator关键字的使用

    让对象之间也能进行相加,比较等操作。

    //对象关系运算符重载 operator关键字的使用
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    private:
        int id = 0;
        int age = 18;
        int money = 100;
        string name = "lihua";
    public:
        Person() = default;
    
        Person(int id, int age, int money, string name) : id(id), age(age), money(money), name(std::move(name)) {}
    
        //成员函数实现 + 号运算符重载
        Person operator+(const Person &p) const {
            Person temp;
            temp.id = id + p.id;
            temp.age = age + p.age;
            temp.money = money + p.money;
            temp.name = name +"-"+ p.name;
            return temp;
        }
    
        // 重写==
        bool operator==(const Person &p) const {
            return money == p.money;
        }
        //可以重写各种运算符。
    
    
    
        int getId() const {
            return id;
        }
    
        void setId(int id) {
            Person::id = id;
        }
    
        int getAge() const {
            return age;
        }
    
        void setAge(int age) {
            Person::age = age;
        }
    
        int getMoney() const {
            return money;
        }
    
        void setMoney(int money) {
            Person::money = money;
        }
    
        const string &getName() const {
            return name;
        }
    
        void setName(const string &name) {
            Person::name = name;
        }
    };
    
    void printf(Person &person){
        cout << "id="<<person.getId()<<" ;"<< "age="<<person.getAge()<<" ;"<< "name="<<person.getName()<<" ;"<< "money="<<person.getMoney()<<endl;
    }
    
    int main() {
        Person person1 = Person(1, 18, 100, "li");
        Person person2 = Person(2, 20, 100, "feng");
        Person p = person1 + person2;
        printf(p);
        if (person1 == person2) {
            cout << "两者的钱一样" << endl;
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    更多可以参考这篇文章

    (八)继承

    1. 如何继承

    类继承的格式 class 子类 : public 父类 ; public 表示继承父类那些封装类型的属性和方法,也就是继承类型。一共有三种继承类型:公共继承、保护继承、私有继承。

    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : public Father {
    private:
        string hobby;
        string school;
    public:
        void mySchool() {
            cout << "我在" << school << "上学。" << endl;
        }
    
        const string &getHobby() const {
            return hobby;
        }
    
        void setHobby(const string &hobby) {
            Son::hobby = hobby;
        }
    
        const string &getSchool() const {
            return school;
        }
    
        void setSchool(const string &school) {
            Son::school = school;
        }
    };
    
    
    int main() {
    
        Son son;
        son.name = "小明";
        son.money = 0;
        son.age = 18;
        //可以调用父类的方法。
        son.say();
        son.setSchool("星星小学");
        son.mySchool();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    2. 继承类型

    三种继承方式:

    • 公共继承:子类内只能访问父类封装类型为 public 的属性和方法。同时子类继承到的属性方法的封装类型变为:public。
    • 保护继承:子类内能访问父类封装类型为 public和protected 的属性和方法。同时子类继承到的属性方法的封装类型变为:protected。
    • 私有继承:子类内能访问父类封装类型为 public和protected 的属性和方法。同时子类继承到的属性方法的封装类型变为:private。
    1. public
    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
        const string &getHobby() const {
            return hobby;
        }
    protected:
        string house;
        const string &getHouse() const {
            return house;
        }
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : public Father {
    public:
        void test(){
           Son son;
           //son.
        }
    };
    
    
    int main() {
        Son son;
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述

    1. protected
    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
        const string &getHobby() const {
            return hobby;
        }
    protected:
        string house;
        const string &getHouse() const {
            return house;
        }
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : protected Father {
    public:
        void test(){
           Son son;
           //son.
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    1. private
    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
        const string &getHobby() const {
            return hobby;
        }
    protected:
        string house;
        const string &getHouse() const {
            return house;
        }
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : private Father {
    public:
        void test(){
           Son son;
           son.
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    3. 继承后,子类和父类的构造函数和析构函数调用顺序

    对于构造函数,先调用父类再调用子类。

    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
        const string &getHobby() const {
            return hobby;
        }
    protected:
        string house;
        const string &getHouse() const {
            return house;
        }
    public:
        string name;
        int age;
        int money;
    
        Father() {
            cout << "父类构造函数。" << endl;
        }
    
        virtual ~Father() {
            cout << "父类析构函数。" << endl;
        }
    
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : private Father {
    public:
        Son() {
            cout << "子类构造函数。" << endl;
        }
    
        virtual ~Son() {
            cout << "子类析构函数。" << endl;
        }
    };
    
    
    int main() {
        Son son;
        
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    在这里插入图片描述

    4. 重写父类的函数(函数、变量重名)
    1. 当子类有函数与父类一样时,子类会重写父类提供的函数。(默认用自己写的,不用父类提供的)
    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
        const string &getHobby() const {
            return hobby;
        }
    protected:
        string house;
        const string &getHouse() const {
            return house;
        }
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : public Father {
    public:
        //重写say函数
        void say() const {
            cout << "子类-》我的名字是" << name << endl;
        }
    };
    
    
    int main() {
        Son son;
        son.say();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    发现调用的是子类的say函数。

    在这里插入图片描述

    想要调用父类的怎么办?只需要在函数前面标明是父类的。son.Father::say();

    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
        const string &getHobby() const {
            return hobby;
        }
    protected:
        string house;
        const string &getHouse() const {
            return house;
        }
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : public Father {
    public:
        //重写say函数
        void say() const {
            cout << "子类-》我的名字是" << name << endl;
        }
    };
    
    
    int main() {
        Son son;
        son.Father::say();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    5. 多重继承

    c++中是能多重继承的,也就是子类可以有多个父类。

    格式:class 子类 :封装类型 父类1 , 封装类型 父类2 。比如:class Son : public Father1,public Father2 ;

    注意:多重继承,父类中可能出现一样的方法和变量,所以要加前缀区分。

    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father1 {
    private:
        string hobby;
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Father2 {
    private:
        string hobby;
    public:
        string name;
        int age;
        int money;
        void say() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    class Son : public Father1,public Father2 {
    public:
        void test() const {
            //当函数、变量名一样时,用前缀加以区分。
            cout << "子类-》我的名字是" << Father1::name << endl;
            Father2::say();
        }
    };
    
    
    int main() {
        Son son;
        son.test();
        //当函数、变量名一样时,用前缀加以区分。
        son.Father1::say();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    6. 菱形继承

    菱形继承概念:

    ​ 两个派生类A、B继承同一个基类F

    ​ 又有某个类C同时继承者两个派生类A、B。(这时候C就会间接继承两个F。有两个一样的爷爷。因此在调用时,就不知道调用哪个爷爷。)

    ​ 这种继承被称为菱形继承,或者钻石继承。

    可以使用 virtual 关键字解决上面产生的问题。

    class Animal
    {
    public:
    	int m_Age;
    };
    
    //继承前加virtual关键字后,变为虚继承
    //此时公共的父类Animal称为虚基类
    class Sheep : virtual public Animal {};
    class Tuo   : virtual public Animal {};
    class SheepTuo : public Sheep, public Tuo {};
    
    void test01()
    {
    	SheepTuo st;
    	st.Sheep::m_Age = 100;
    	st.Tuo::m_Age = 200;
    
    	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
    	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
    	cout << "st.m_Age = " << st.m_Age << endl;
    }
    
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    7. 多态

    什么是多态?简单来说就是,将一个子类的对象,向上转型为父类。比如 :Father father = Son(); 。这样将Father father作为函数的参数,那么在调用函数时,可以传入各种各样的子类的对象

    多态满足条件

    • 有继承关系
    • 子类重写父类中的虚函数

    多态使用条件

    • 父类指针或引用指向子类对象
    //继承学习
    #include 
    
    using namespace std;
    
    
    class Father {
    private:
        string hobby;
    public:
        string name;
        int age;
        int money;
    
        virtual void beat() const {
            cout << "我的名字是" << name << endl;
        }
    };
    
    
    class Son1 : public Father {
    public:
        void beat() const override {
            cout << "Son1-> 被打" << endl;
        }
    };
    
    class Son2 : public Father {
    public:
        void beat() const override {
            cout << "Son2-> 被打" << endl;
        }
    };
    
    
    //父亲打儿子,可以选择打哪个儿子。
    void beatSon(const Father& son){
        son.beat();
    }
    
    int main() {
        Son1 son1;
        beatSon(son1);
    
        Son2 son2;
        beatSon(son2);
    
        //当函数、变量名一样时,用前缀加以区分。
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    (九)virtual (抽象类,接口)

    一个简单的例子。

    #include 
    
    using namespace std;
    class Parent
    {
    public:
        void say()
        {
            cout << "Parent say" << endl;
        }
    };
    
    class Child1 : public Parent
    {
    public:
        void say()
        {
            cout << "Child1 say" << endl;
        }
    };
    
    class Child2 : public Parent
    {
    public:
        void say()
        {
            cout << "Child2 say" << endl;
        }
    };
    
    void call(Parent* p)
    {
        p->say();
    }
    
    int main()
    {
    
        Child1 c1;
        Child2 c2;
        call(&c1);
        call(&c2);
    
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在这里插入图片描述
    看到输出大为吃惊,跟java的多态不一样。我以为是输出:

    Child1 say
    Child2 say
    
    • 1
    • 2

    导致错误输出的原因是,调用函数 say() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 say() 函数在程序编译期间就已经设置好了。

    修改一下父类的say函数。

    class Parent
    {
    public:
        virtual  void say()
        {
            cout << "Parent say" << endl;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    得到正确的输出。
    在这里插入图片描述
    上面父类的virtual void say() 为虚函数,如果虚函数只有声明,没有实现(函数体),那么改父类,不能被实例化。

    class Parent
    {
    public:
        virtual void say();
    };
    int main()
    {
    	//不能实例化,会报错。
        Parent p;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上面say函数,称为纯虚函数。不过一般不这么写,推荐写成 virtual void say() =0 ;有纯虚函数的类一般称为抽象类,接口

  • 相关阅读:
    Arduino与Proteus仿真-Nokia5110 LCD界面菜单仿真
    【操作系统】I/O 管理(二)—— I/O 核心子系统
    统计字符串中不同回文子序列的个数
    mysql快问快答(1)---MySQL常用的存储引擎MyISAM和InnoDB
    web前端期末大作业 html+css学生心理 7页主题网页设计
    SpringCloudAlibaba-微服务-注册中心之Nacos安装启动与集群配置
    加锁和解锁-ReentrantLock详解-AQS-并发编程(Java)
    解决 MyBatis-Plus 中 ID 自增问题
    rtx3090ti和rtx3090的区别 3090ti和3090算力对比
    java word 转html 的两种方法
  • 原文地址:https://blog.csdn.net/a123123sdf/article/details/126555408