• c++中的常用知识点总结


    命名空间

    使用命名空间之后,调用代码时可以省去也可以不省去相关的前缀。

    #include 
    
    using namespace std;//使用c++自己的命名空间
    int main() {
        int num1 = 10;
    
        std::cout << "Hello, World!" << std::endl;
        cout<<num1<<endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    自定义命名空间

    // 命名空间
    
    #include 
    
    // 声明std,我们的main函数就可以直接使用里面的成员,不需要使用 std::
    using namespace std; // C++自己的命名空间 (C# .net 命名空间)
    
    // 自定义命名空间
    namespace derry1 {
        int age = 33;
        char * name = "Derry猛男1";
    
        void show() {
            cout << "name:" << name << ", age:" << age << endl;
        }
    
        void action() {
            cout << "derry1 action" << endl;
        }
    }
    
    // TODO ------ 命名空间里面重复的函数
    // 自定义命名空间
    namespace derry2 {
        void action() {
            cout << "derry2 action" << endl;
        }
    }
    
    // TODO ------ 小概率会遇到的情况,命名空间的嵌套
    // 自定义命名空间
    namespace derry3 {
        namespace derry3Inner {
            namespace derry3Inner1 {
                namespace derry3Inner2 {
                    namespace derry3Inner3 {
                        void out() {
                            cout << "爱恨情仇人消瘦,悲欢起落人寂寞" << endl;
                        }
                    }
                }
            }
        }
    }
    
    // 声明各个写的 命名空间
    // using namespace derry1;
    
    int main() {
        cout << "命名空间" << endl;
    
        // 声明各个写的 命名空间
        using namespace derry1;
    
        int ageValue = derry1::age; // 方式1 使用 刚刚声明的命名空间
        derry1::show(); // 使用 刚刚声明的命名空间
    
        ageValue = age; // 方式2 直接去引出来 ::
        show(); // 直接去引出来 ::
    
    
        // TODO ------ 命名空间里面重复的函数
        using namespace derry2;
        // action(); 很尴尬
        derry1::action();
        derry2::action();
    
        // TODO ------ 小概率会遇到的情况,命名空间的嵌套
    
        // 第一种方式 先声明命名空间  再使用
        using namespace derry3::derry3Inner::derry3Inner1::derry3Inner2::derry3Inner3;
        // 再使用
        out();
    
        // 第二种方式 直接使用
        derry3::derry3Inner::derry3Inner1::derry3Inner2::derry3Inner3::out();
    
        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

    • 头文件:Student.h
    //
    // Created by sunteng on 2023/9/5.
    //
    
    #ifndef TEST_STUDENT_H
    #define TEST_STUDENT_H
    
    #endif //TEST_STUDENT_H
    
    class Student{
    
    private:
        //下面的成员和函数,都是私有
        char * name;
        int age;
    
    public:
        //下面的成员和函数,都是公有
        void setAge(int age);
        void setName(char *name);
        int getAge();
        char * getName();
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 类文件:Student.cpp
    //
    // Created by sunteng on 2023/9/5.
    //
    
    
    #include "Student.h"
    
    // 根据 Student.h 头文件 ,写实现
    
    // 和实现头文件那个函数,没有任何关系,相当于另外一个函数
    /*void setAge(int age) {
    
    }*/
    
    void Student::setAge(int age) {
        //this是一个Student类型的指针
        this->age = age;
    }
    
    void Student::setName(char *name) {
        this->name = name;
    }
    
    int Student::getAge() {
        return this->age;
    }
    
    char * Student::getName() {
        return this->name;
    }
    
    • 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
    • main
    #include 
    #include "Student.h"//引入头文件即可
    using namespace std;
    int main() {
    
        Student student1;//栈区开辟空间
    
        //赋值
        student1.setName("brett");
        student1.setAge(19);
    
        cout<<"name:"<<student1.getName()<<" ,age:"<<student1.getAge()<<endl;
    
        Student* student2 = new Student();//new 堆区分配空间
    
        student2->setAge(88);
        student2->setName("mike");
    
        cout<<"name:"<<student2->getName()<<" ,age:"<<student2->getAge()<<endl;
    
    
        //堆区分配的对象使用完之后必须手动释放
        if(student2){
            delete student2;
            student2 = NULL;
        }
        // new/delete 是一套  会调用构造函数 与 析构函数   【C++标准规范】
        // malloc/free是一套  不调用构造函数 与 析构函数 【C的范畴,虽然不推荐,但是也是可以的】
    
    
        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
    • 构造函数和析构函数
    
    #include 
    #include 
    using namespace std;
    
    class Student {
    
    // 构造函数
    public:
        // 空参数构造函数
        Student() {
            cout << "空参数构造函数" << endl;
        }
    
        // 一个参数的构造函数
        // :Student(name, 87) 等价 1.调用两个参数的构造函数, 2.再调用当前函数
        // 学生说的:先调用两个参数的,再调用一个的
        Student(char *name) :Student(name, 87) {
            cout << "一个参数的构造函数" << endl;
            if(!this->name){//防止重复赋值
                this->name = name;
            }
    
        }
    
        // 两个参数的构造函数
        Student(char *name, int age) {
            // this->name = name;
    
            // 堆区
            this->name = (char *) (malloc(sizeof(char *) * 10));
            strcpy(this->name, name);
    
            this->age = age;
            cout << "两个参数的构造函数" << endl;
        }
    
        // 析构函数 Student对象的,临终遗言,Student对象被回收了,你做一些释放工作
        // delete stu 的时候,我们的析构函数一定执行
        // free不会执行析构函数,也意味着,你没法在析构函数里面,做释放工作, malloc也不会调用构造函数
        ~Student() {
            cout << "析构函数" << endl;
    
            // 必须释放 堆区开辟的成员
            if (this->name) {
                free(this->name);
                this->name = NULL; // 执行NULL的地址,避免出现悬空指针
            }
        }
    
    // 私有属性
    private:
        char *name;
        int age;
    
    // 公开的 set get 函数
    public:
        int getAge() {
            return this->age;
        }
    
        char *getName() {
            return this->name;
        }
    
        void setAge(int age) {
            this->age = age;
        }
    
        void setName(char *name) {
            this->name = name;
        }
    };
    
    int main() {
    
    
        // TODO =========== 下面是堆区 开辟空间的  堆区必须手动释放,否则内存占用越来
    
        // 系统源码中,会看到,很多使用 new 关键字
    
        // *stu  ->:调用一级指针的成员
        // new/delete
        // C++中,必须使用 new/delete 一套
        Student *stu = new Student("杜子腾");
        cout << "name:" << stu->getName() << ", age:" << stu->getAge() <<  endl;
        delete stu;
    
    
        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
    • 88
    • 89
    • 90
    • 91
    • 92
    • 拷贝构造函数与构造函数
    // 4.拷贝构造函数。
    
    #include 
    #include 
    
    using namespace std;
    
    class Student {
    
    public:
        Student() { cout << "空参数构造函数" << endl; }
    
        // 两个参数的构造函数
        Student(char *name, int age) : name(name), age(age) {
            cout << "两个参数构造函数" << endl;
        }
    
        // 析构函数
        // ~Student(char * name) { } 这样写,就不是析构函数了,如果你这样写,C/C++编译器对你很无语
        ~Student() {
            cout << "析构函数" << endl;
        }
    
        // 拷贝构造函数,它默认有,我们看不到,一旦我们写拷贝构造函数,会覆盖她
        // 对象1 = 对象2
        // 覆盖拷贝构造函数
        Student(const Student & student) { // 常量引用:只读的,不让你修改
            cout << "拷贝构造函数" << endl;
    
            // 我们自己赋值
            // 为什么要自己赋值,自己来控制,就可以 例如:-10
            this->name = student.name;
            this->age = student.age - 10;
    
            cout << "自定义拷贝构造函数 内存地址 " << &student << endl;
        }
    
    // 私有属性
    private:
        char *name;
        int age;
    
    // 公开的 set get 函数
    public:
        int getAge() {
            return this->age;
        }
    
        char *getName() {
            return this->name;
        }
    
        void setAge(int age) {
            this->age = age;
        }
    
        void setName(char *name) {
            this->name = name;
        }
    };
    
    struct Person {
        int age;
        char *name;
    };
    
    // TODO = 号的意义 隐士代码,引出 拷贝构造函数
    
    int main() {
        Person person1 = {100, "张三丰"};
    
        // = 你看起来,没有什么特殊,隐士的代码:你看不到  C/C++编译器 会把p1的成员值赋值给p2成员
        Person person2 = person1;
    
        cout << &person1 << endl;
        cout << &person2 << endl;
    
        cout << person2.name << ",  " << person2.age << endl;
    
        // 思考:对象 对象1=对象2  默认的 拷贝构造函数
    
        return 0;
    }
    
    // TODO  拷贝构造函数
    
    /*int main() {
        Student stu1("李鬼", 34);
        Student stu2 = stu1;
    
        cout << stu2.getName() << " , " <<  stu2.getAge() <<  endl;
        cout << "main " << &stu1 << endl; // 地址的打印是一样的,  注意:cnetos的环境 地址打印有差异,要注意
    
    
    
        // TODO  拷贝构造函数的注意点:
        // Student stu1("李鬼", 34);
    
        // Student stu2;
        // stu2 = stu1; // 这样赋值是不会调用 自定义拷贝构造函数,但是会调用默认赋值
        // Student stu2 = stu1;  // 这样赋值是会调用 自定义拷贝构造函数,我们自己赋值
    
        // cout << stu2.getName() << " , " <<  stu2.getAge() <<  endl;
    
        getchar(); // 程序等待在这一行
    
        return 0;
    } // main函数弹,stu1栈成员会回收  stu2栈成员会回收
    */
    
    // TODO 这种写法 拷贝构造函数  到底会不会调用
    /*int main() {
        Student *student1 = new Student("杜子腾", 39);
    
        Student *student2 = student1;  // 压根就不会执行拷贝构造函数(指针指向问题,和我们刚刚那个  对象2=对象1 是两回事)
    
        student2->setAge(99);
    
        cout << student1->getName() << student1->getAge() << 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
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 深拷贝与浅拷贝
    #define _CRT_SECURE_NO_WARNINGS // strcpy运行会报错,支持
    
    #include
    #include
    using namespace std;
    
    class Student
    {
    public:
    
        int age;
        char * name;
    
        Student() { cout << "空参数构造函数" << endl; }
    
        Student(char * name) :Student(name, 99) {
            cout << "一个参数构造函数 this:" << this << endl;
        }
    
        Student(char * name, int age) {
            cout << "二个参数构造函数 this:" << this << endl;
    
            this->name = (char *)malloc(sizeof(char *)* 10);
            strcpy(this->name, name);
    
            this->age = age;
        }
    
        ~Student() {
            cout << "析构函数执行 &this->name:" << this->name << endl;
    
            free(this->name);
            this->name = NULL;
        }
    
        // 默认有一个拷贝构造函数 隐士的 我们看不见
        // 一旦复写了拷贝构造函数,默认的还在吗? Java的构造函数一个思路
        // 自定义拷贝构造函数 如果有堆成员,必须采用深拷贝
        Student(const Student & stu) {
            // stu 旧地址
    
            // this 新地址
    
            // s2 = 新地址
    
            cout << "拷贝构造函数 &stu:" << &stu << " this:" << this << endl;//二者地址不一样
    
            // 【浅拷贝】:新地址name  旧地址name 指向同一个空间,会造成,重复free的问题,引发奔溃
            // 新地址name = 旧地址 (浅拷贝)
            // this->name = stu.name;
    
            // 【深拷贝】
            this->name = (char *)malloc(sizeof(char *)* 10);
            strcpy(this->name, name);
    
            this->age = stu.age;
    
            cout << "拷贝构造函数2 this->name:" << (this->name) << "  stu.name:" << stu.name << endl;
    
            // 深拷贝 后面见  原理全部打通的时候讲
        } // 此拷贝构造函数执行完 旧会出现一个 this==新地址  给 main函数的 stu
    
    
        // 默认的拷贝构造函数 是浅拷贝
    };
    
    void showStudent(Student stu) {
        cout << "showStudent函数:" << &stu << "  " << stu.name << "," << stu.age<< endl;
    }
    
    int main() {
        Student stu("刘奋", 31);
    
        showStudent(stu); // 弹栈后 新地址name释放一遍
        showStudent(stu);
    //
    //    showStudent(stu);
    //
    //
    //    showStudent(stu);
    //
    //
    //    showStudent(stu);
    
        getchar();
        return 0;
    } // main函数弹栈 stu 旧地址
    
    • 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

    指针常量 常量指针 常量指针常量

    // 指针常量 常量指针 常量指针常量
    
    #include 
    #include 
    #include 
    
    using namespace std;
    
    int main() {
    
        // *strcpy (char *__restrict, const char *__restrict);
        // strcpy()
    
    
        int number = 9;
        int number2 = 8;
    
        // 大道至简 一分钟搞定
    
        // 常量指针
        const int * numberP1 = &number;
        // *numberP1 = 100; // 报错,不允许去修改【常量指针】存放地址所对应的值
        // numberP1 = &number2; // OK,允许重新指向【常量指针】存放的地址
    
        //  指针常量
        int* const numberP2 = &number;
        *numberP2 = 100; // OK,允许去修改【指针常量】存放地址所对应的值
        // numberP2 = &number2; // 报错,不允许重新指向【指针常量】存放的地址
    
        // 常量指针常量
        const int * const numberP3 = &number;
        // *numberP3 = 100; // 报错,不允许去修改【常量指针常量】存放地址所对应的值
        // numberP3 = &number2; // 报错,不允许重新指向【常量指针常量】存放的地址
    
        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

    运算符重载

    // 2.类里运算符重载。
    
    #include 
    using namespace std;
    
    class Derry {
    private:
        int x,y;
    
    public:
        Derry() {
    
        }
    
        // 系统C++源码,大量使用此方式 :x(x), y(y)
        Derry(int x, int y) :x(x), y(y) {}
    
        // set get 函数
        void setX(int x) {
            this->x = x;
        }
        void setY(int y) {
            this->y = y;
        }
        int getX() {
            // this->x  -9; 系统怕你在函数里面 修改了
            return this->x;
        }
        int getY() {
            return this->y;
        }
    
        // +号,运算符重载
        /*Derry operator + (Derry derry1) {
            // this指针 指向当前对象,所以只需要一个
            int x = this->x + derry1.getX();
            int y = this->y + derry1.getY();
            return Derry(x, y);
        }*/
    
        // 系统是这样写的  常量引用:不允许修改,只读模式
        // const 关键字的解释
        // & 性能的提高,如果没有&  运行+ 构建新的副本,会浪费性能
        // 如果增加了& 引用是给这块内存空间取一个别名而已
        Derry operator + (const Derry& derry1) {
            // this指针 指向当前对象,所以只需要一个
            int x = this->x + derry1.x; // 我在类的里面,是可以拿私有成员的
            int y = this->y + derry1.y; // 我在类的里面,是可以拿私有成员的
            return Derry(x, y);
        }
    
        // 运算符- 重载
        Derry operator - (const Derry & derry1) {
            int x = this->x - derry1.x;
            int y = this->y - derry1.y;
            return Derry(x, y);
        }
    };
    
    int main() {
        Derry derry1(1000, 2000);
        Derry derry2(3000, 4000);
    
        // Derry result = derry1 + derry2;
    
         Derry result = derry2 - derry1;
    
        cout << result.getX() << " , " << result.getY() << 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

    二义性与虚基类

    // 多继承 二义性2:
    // 在真实开发过程中,严格避免出现 二义性
    
    #include 
    
    using namespace std;
    
    // 祖父类
    class Object {
    public:
        int number;
    };
    
    // 父类1
    class BaseActivity1 : public Object {
    
    };
    
    // 父类2
    class BaseActivity2 : public Object {
    
    };
    
    // 子类
    class Son : public BaseActivity1, public BaseActivity2 {
    
        // 第二种解决方案: 在类中定义同名成员,覆盖掉父类的相关成员
    public:
        int number;
    
    };
    
    
    int main() {
        Son son;
    
        // error: request for member 'show' is ambiguous  二义性 歧义
        // son.number = 2000;
    
        // 第一种解决方案: :: 明确指定
        son.BaseActivity1::number  = 1000;
        son.BaseActivity2::number  = 1000;
    
        // 第二种解决方案: 在类中定义同名成员,覆盖掉父类的相关成员
        son.number = 3000;
    
        // 第三种解决方案: 【虚基类】 属于 虚继承的范畴
    
        return 0;
    }
    
    //第三种方式;虚基类
    
    // 第三种解决方案: 【虚基类】 属于 虚继承的范畴
    // 真实C++开始,是很少出现,二义性(歧义) 如果出现, 系统源码(系统用 第三种解决方案)
    
    #include 
    
    using namespace std;
    
    // 祖父类
    class Object{
    public:
        int number;
        void show() {
            cout << "Object show run..." << endl;
        }
    };
    
    // 父类1
    class BaseActivity1 : virtual public Object {
    };
    
    // 父类2
    class BaseActivity2 : virtual public Object {
    };
    
    // 子类
    class Son : public BaseActivity1, public BaseActivity2 {
    
    };
    
    int main() {
        Object object;
        BaseActivity1 baseActivity1;
        BaseActivity2 baseActivity2;
        Son son;
    
        object.number = 100;
        baseActivity1.number = 200;
        baseActivity2.number = 300;
        son.number = 400;
    
        object.show();
        baseActivity1.show();
        baseActivity2.show();
        son.show();
    
        cout << object.number << endl;
        cout << baseActivity1.number << endl;
        cout << baseActivity2.number << endl;
        cout << son.number << 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
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 虚基类

    在C++中,虚基类是用于解决多继承中的菱形继承问题的一种机制。菱形继承指的是一个派生类同时继承自两个或多个基类,而这些基类又共同继承自同一个基类,形成了一个菱形的继承结构。

    虚基类的作用是解决菱形继承带来的二义性和冗余的问题。当一个派生类通过多条路径继承自同一个基类时,如果不使用虚基类,那么在派生类中就会存在多个基类子对象的实例,这样就会导致同名成员在派生类中出现冗余,访问这些成员时会产生二义性。

    通过使用虚基类,可以确保在派生类中只有一个基类子对象的实例,从而避免了冗余和二义性。虚基类的成员在派生类中只有一份拷贝,这样就可以确保派生类对基类成员的访问是唯一的。

    使用虚基类的语法是在派生类的基类列表中,将虚基类声明为虚基类。例如:

    class Base {
    public:
        // Base类的成员
    };
    
    class Derived1 : virtual public Base {
    public:
        // Derived1类的成员
    };
    
    class Derived2 : virtual public Base {
    public:
        // Derived2类的成员
    };
    
    class Derived3 : public Derived1, public Derived2 {
    public:
        // Derived3类的成员
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在上面的例子中,Base是虚基类,Derived1和Derived2都通过虚继承方式继承自Base。Derived3通过多继承同时继承自Derived1和Derived2,这样就避免了菱形继承带来的问题。

    总结来说,虚基类的作用是解决多继承中的菱形继承问题,避免冗余和二义性,确保派生类对基类成员的访问是唯一的。

    公有继承与私有继承

    
    #include 
    
    using namespace std;
    
    class Person {
    public:
        char *name;
        int age;
    
    public:
        Person(char *name, int age) : name(name) {
            this->age = age;
            cout << "Person 构造函数" << endl;
        }
    
        void print() {
            cout << this->name << " , " << this->age << endl;
        }
    };
    
    // 1.默认是 隐式代码: : private Person
    // 2.私有继承:在子类里面是可以访问父类的成员,但是在类的外面不行
    // 3.必须公开继承,才可以访问父类的成员
    class Student : public Person {
    
    // 类 默认是私有,注意下
    
    private:
        char * course;
    
    public:
        // :父类 , 给自己子类成员初始化
        Student(char * name, int age, char* course) : Person(name, age) , course(course) {
            cout << "Student 构造函数" << endl;
        }
    
        void test() {
            cout << name << endl;
            cout << age << endl;
            print();
        }
    };
    
    int main() {
        Student stu("李元霸", 99, "C++");
    
        // 公开继承,才可以拿父类的成员
        stu.name = "李四";
        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

    静态类

    // 2.C++static关键字。 正确的写法
    
    /**
     * 静态的总结:
     * 1.可以直接通过类名::静态成员(字段/函数)
     * 2.静态的属性必须要初始化,然后再实现(规则)
     * 3.静态的函数只能取操作静态的属性和方法
     */
    
    #include 
    
    using namespace std;
    
    class Dog {
    public:
        char * info;
        int age;
    
        // 先声明
        static int id;
    
        static void update() {
            id += 100;
    
            // 报错:静态函数不能调用非静态函数
            // update2();
        }
    
        void update2() {
            id = 13;
        }
    };
    
    // 再实现
    int Dog::id = 9;
    
    int main() {
        Dog dog;
        dog.update2(); // 普通函数
        Dog::update(); // 静态函数
        dog.update(); // 对象名.静态函数(一般都是使用::调用静态成员,这种方式可以 知道就行)
    
        cout << Dog::id << 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

    友元类和友元函数

    在C++中,友元类和友元函数是一种特殊的访问权限控制机制,允许一个类或函数访问另一个类的私有成员。

    友元类(Friend Class)是指在一个类中声明另一个类为友元,从而使得被声明的类可以访问声明它为友元的类的私有成员。友元类的声明通常出现在类的定义中,例如:

    class A {
        friend class B; // B是A的友元类
    private:
        int privateData;
    };
    
    class B {
    public:
        void accessPrivateData(A& obj) {
            int data = obj.privateData; // 可以访问A类的私有成员
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在上面的例子中,类B被声明为类A的友元类,因此类B可以访问类A的私有成员privateData。

    友元函数(Friend Function)是指在一个类中声明一个函数为友元,从而使得被声明的函数可以访问声明它为友元的类的私有成员。友元函数的声明通常出现在类的定义中,例如:

    class A {
        friend void printPrivateData(A& obj); // printPrivateData是A的友元函数
    private:
        int privateData;
    };
    
    void printPrivateData(A& obj) {
        int data = obj.privateData; // 可以访问A类的私有成员
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在上面的例子中,函数printPrivateData被声明为类A的友元函数,因此函数printPrivateData可以访问类A的私有成员privateData。

    需要注意的是,友元类和友元函数破坏了封装性,因此应该谨慎使用。过度使用友元可能会导致代码的可维护性和可读性下降。在设计类的时候,应该优先考虑使用成员函数和访问修饰符(如public、private、protected)来实现对类成员的访问控制。只有在确实需要访问私有成员的特殊情况下,才考虑使用友元类或友元函数。

    // 友元函数
    
    // 老外:你是它的好朋友,那就可以拿私有成员给好朋友
    
    #include 
    
    using namespace std;
    
    class Person {
    private: // 私有的age,外界不能访问
        int age = 0;
    
    public:
        Person(int age) {
            this->age = age;
        }
    
        int getAge() {
            return this->age;
        }
    
        // 定义友元函数 (声明,没有实现)
        friend void updateAge(Person * person, int age);
    };
    
    // 友元函数的实现,可以访问所以私有成员
    void updateAge(Person* person, int age) {
        // 默认情况下:不能修改 私有的age
        // 谁有这个权限:友元(拿到所有私有成员)
        person->age = age;
    }
    
    int main() {
        Person person = Person(9);
        updateAge(&person, 88);
    
        cout << person.getAge() << 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

    可变参数

    #include 
    #include  // 可变参数的支持
    using namespace std;
    
    // Java的可变参数: int ...
    // C++的可变参数写法:...
    
    // count变量的第二个用处,用于循环遍历长度
    void sum(int count, ...) {
        va_list vp; // 可变参数的动作
    
        // 参数一:可变参数开始的动作vp
        // 参数二:内部需要一个 存储地址用的参考值,如果没有第二个参数,内部他无法处理存放参数信息
        va_start(vp, count);
    
        // 到这里后:vp就已经有丰富的信息
    
        for (int i = 0; i < count; ++i) {
            int r = va_arg(vp, int);
            cout << r << endl;
        }
    
        // 越界 系统值 乱码
        // 取出可变参数的一个值 【娶不到后,会取系统值 乱码】
    //   int number  = va_arg(vp, int);
    //    cout << number << endl;
    
        // 关闭阶段(规范:例如:file文件一样 要关闭)
        va_end(vp);
    }
    
    // 1.可变参数
    int main() {
    
        sum(3, 6,7,8); // 真实开发过程的写法
    
        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

    this是什么——指针常量

    class Worker {
    public:
        char * name;
        int age = NULL; // C++中不像Java,Java有默认值, 如果你不给默认值,那么就是系统值 -64664
    
        // int * const  指针常量 指针常量【地址对应的值能改,地址不可以修改】
        // const int *  常量指针 常量指针【地址可以修改,地址对应的值不能改】
    
        // 纠结:原理:为什么可以修改age
        // 默认持有隐士的this【类型 * const this】
        // 类型 * const 指针常量:代表指针地址不能被修改,但是指针地址的值是可以修改的
        void change1() {
            // 代表指针地址不能被修改
            // this = 0x6546;  // 编译不通过,地址不能被修改,因为是指针常量
            // 地址不可以修改
            // this = 0x43563;
    
            // 隐士的this
            // 但是指针地址的值是可以修改的
            // 地址对应的值能改
            this->age = 100;
            this->name = "JJJ";
        }
    
        // 默认现在:this 等价于 const Student * const  常量指针常量(地址不能改,地址对应的值不能改)
        void changeAction() const {
            // 地址不能改
            // this = 0x43563;
    
            // 地址对应的值不能改
            // this->age = 100;
        }
    
        // 原理:修改隐士代码  const 类型 * const 常量指针常量
        void showInfo() const {
            // this->name = "";
            // this->age = 88;
    
            // 只读的
            cout << "age:" << age << endl;
        }
    };
    
    • 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

    多线程

    C++ 11 之后添加了新的标准线程库 std::thread,std::thread 在 头文件中声明,因此使用 std::thread 时需要包含 在 头文件。

    // 演示多线程的CPP程序
    // 使用三个不同的可调用对象
    #include 
    #include 
    using namespace std;
    
    // 一个虚拟函数
    void foo(int Z)
    {
          for (int i = 0; i < Z; i++) {
                cout << "线程使用函数指针作为可调用参数\n";
              }
    }
    
    // 可调用对象
    class thread_obj {
    public:
          void operator()(int x)
          {
                for (int i = 0; i < x; i++)
                  cout << "线程使用函数对象作为可调用参数\n";
              }
    };
    
    int main()
    {
          cout << "线程 1 、2 、3 "
            "独立运行" << endl;
        
          // 函数指针
          thread th1(foo, 3);//不需要显式调用start()方法来启动线程,std::thread对象的构造函数会自动启动线程的执行。
        
          // 函数对象
          thread th2(thread_obj(), 3);
        
          // 定义 Lambda 表达式
          auto f = [](int x) {
                for (int i = 0; i < x; i++)
                  cout << "线程使用 lambda 表达式作为可调用参数\n";
              };
        
          // 线程通过使用 lambda 表达式作为可调用的参数
          thread th3(f, 3);
        
          // 等待线程完成
          // 等待线程 t1 完成
          th1.join();
        
          // 等待线程 t2 完成
          th2.join();
        
          // 等待线程 t3 完成
          th3.join();
        
          return 0;
    }
    
    /*
    线程 1 、2 、3 独立运行
    线程使用函数指针作为可调用参数
    线程使用函数指针作为可调用参数
    线程使用函数指针作为可调用参数
    线程使用函数对象作为可调用参数
    线程使用函数对象作为可调用参数
    线程使用函数对象作为可调用参数
    线程使用 lambda 表达式作为可调用参数
    线程使用 lambda 表达式作为可调用参数
    线程使用 lambda 表达式作为可调用参数
    */
    
    • 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
  • 相关阅读:
    MySQL图形管理工具的安装与使用
    zookeeper的安装与配置和启动闪退问题的解决(win)
    Django使用正则表达式
    Flink1.14 SourceReader概念入门讲解与源码解析 (三)
    2022牛客暑期多校训练营4(总结+补题)
    # CommonJS模块 和 ECMAScript模块
    能否绝地反击
    linux解决报错 libstdc++.so.6: version GLIBCXX_3.4.30 not found
    setState到底是异步还是同步?
    ASP.Net Core创建MVC项目上传多个文件(流方式)
  • 原文地址:https://blog.csdn.net/qq_36828822/article/details/132692629