美文网首页
黑马C++视频笔记《封装、对象属性》

黑马C++视频笔记《封装、对象属性》

作者: 井底蛙蛙呱呱呱 | 来源:发表于2021-01-03 21:24 被阅读0次
    /* 封装
     * 意义:
     *  (1)将属性和行为作为一个整体,表现生活中的事物;
     *  (2)将属性和行为加以权限控制;
     *
     * 类中的属性和行为统一称为成员。属性又可称为成员属性、成员变量;行为又可称为成员函数,成员方法。
     *
     * struct和class都可以用来定义类,区别在于默认权限,其中struct的默认权限为公共权限,而class的默认权限为私有权限。
     *
     * 类中的属性方法访问权限有三种:
     *  (1)public,公共权限,类内类外都能访问;
     *  (2)protected,保护权限,类内和其继承类都能访问;
     *  (3)private,私有权限,仅类内有访问权限;
     *
     */
    
    /* 类和对象
     *
     * 构造函数和析构函数
     *  (1)构造函数用于初始化,在创建对象时为对象的成员赋值,构造函数由编译器自动调用,无需手动调用;
     *  (2)析构函数在对象销毁前系统自动调用,用于清理完成内存回收。
     *  对象的初始化和清理工作是编译器强制要求我们做的事情,若我们不提供构造和析构函数,编译器会提供自己实现的构造函数和析构函数
     *  (编译器提供的构造函数和析构函数是空实现)。
     *
     * 构造函数
     *  语法:类名(){}
     *  (1)构造函数没有返回值也不写void;
     *  (2)函数名称与类名相同;
     *  (3)构造函数有参数,因此可以发生重载;
     *  (4)程序在调用对象(创建/初始化对象)时会自动调用构造函数,无需手动调用,而且只会调用一次。
     *
     * 构造函数的分类及调用
     *  两种分类方式:
     *      (1)按参数分为:有参构造和无参构造,即`类名(para){}`和`类名(){}`;
     *      (2)按类型分为:普通构造和拷贝构造,即`类名([有/无参数]){}`和`类名(const 类名 &c){}`。拷贝构造函数的参数应该为const引用方式传参;
     *
     *  三种调用方式:
     *      (1)括号法,如
     *          Person p1; // 调用默认构造函数,不能使用Person p1(),否则编译器会认为这是一个函数的声明
     *          Person p2(param); // 调用有参构造函数
     *          Person p3(p1); // 调用拷贝构造函数
     *      (2)显示法;
     *          Person p1; // 无参构造
     *          Person p2 = Person(10); // 有参构造,右边为匿名对象,左边相当于给匿名对象起了个名字以方便后续使用;
     *          Person p3 = Person(p1); // 拷贝构造,右边为匿名对象,左边相当于给匿名对象起了个名字以方便后续使用;
     *
     *          Person(10); // 匿名对象,特点:当前执行结束后,系统会立即回收掉匿名对象
     *          注:不要利用拷贝构造函数初始化匿名对象,编译器会认为`Person(p3) === Person p3`,自动转换为了默认(无参)构造函数;
     *      (3)隐式转换法;
     *          Person p4 = 10; // 有参构造,相当于写了`Person p4 = 10`
     *          Person p5 = p4; // 拷贝构造,相当于写了`Person p5 = p4`
     *
     *  拷贝构造函数的调用时机,通常有三种情况:
     *   (1)使用一个已经创建完毕的对象来初始化一个新对象,即 `Person p2(p1)`;
     *   (2)值传递的方式给函数参数传值, 即 `func(Person p)`,由于是值传递,因此在传进函数内部时,其实是对p这个对象进行了一次完整的拷贝;
     *   (3)以值方式返回局部对象,`void func(){Person p; return p;}`, 局部对象只存在于函数域内,函数执行完就回收了,因此返回的是局部对象的一个拷贝。
     *
     *  构造函数调用规则。默认情况下,c++编译器至少给一个类添加三个函数:
     *  (1)默认构造函数(无参,函数体为空);
     *  (2)默认析构函数(无参,函数体为空);
     *  (3)默认拷贝构造函数,对属性进行值拷贝;
     *  构造函数调用规则如下:
     *  (1)如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造;
     *  (2)如果用户定义拷贝构造函数,c++不会再提供其它构造函数。
     *
     * 深拷贝与浅拷贝:
     *  - 浅拷贝:简单的赋值拷贝操作;
     *  - 深拷贝:在堆区重新申请空间,进行拷贝工作。堆区对象不会自动进行回收,需要手动释放,否则要等到整个程序完了才会进行回收释放内存。
     * 因此,如果属性有在堆区开辟的,即 `int * attr =  new int(10)`, 类属性attr是一个指针,其值是一个地址。拷贝函数拷贝时候也是拷贝了这个地址,
     * 当一个拷贝类完成任务被释放析构时候会删除掉这个内存地址,后续被拷贝类实例被释放时就会出现错误,因为其属性attr已经不存在,两者释放的是同一个地址,报错。
     *
     * 初始化列表,可当做构造函数的一种写法,用来初始化属性。
     * 语法:构造函数(): 属性1(值1),属性2(值2)... {}.
     * 栗子:`Person(int a, int b, int c): m_A(a), m_B(b), m_C(c){}`.
     *
     * 当其他类对象作为本类成员,构造时先构造其他类对象,再构造自身,析构的顺序与构造函数相反。
     *
     * 析构函数
     *  语法:~类名(){}
     *  (1)析构函数没有返回值也不写void;
     *  (2)函数名称与类名相同,在名称前加上符号~;
     *  (3)析构函数不可以有参数,因此【不可以】发生重载;
     *  (4)程序在对象销毁前会自动调用析构函数,无需手动调用,而且只会调用一次。
     *
     *
     * 静态成员:静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
     * 静态成员分为两种:
     * (1)静态成员变量
     *      - 所有对象共享同一份数据;
     *      - 在编译阶段分配内存;
     *      - 类内声明,类外初始化;
     * (2)静态成员函数
     *      - 所有对象共享同一个函数;
     *      - 静态成员函数只能访问静态成员变量。
     *      静态成员函数有两种访问方式:通过对象访问和通过类名访问(和python的静态函数是一样的)。
     *
     *
     * 类的成员函数和成员变量是分开储存的,其中:
     *  - 非静态成员变量,属于类的对象上(注:类对象即实例化的类);
     *  - 静态成员变量,不属于类对象上;
     *  - 非静态成员函数,不属于类对象上;
     *  - 静态成员函数,不输于类对象上。
     *
     * this指针
     * 引子:C++中成员变量和成员函数是分开储存的,则每一个非静态函数只会诞生一份函数实例,也即多个同类型对象会共用一块代码,
     * 那么这一块代码如何区分是哪个对象调用的自己?
     * 答:C++通过提供特殊的对象指针,this指针来解决此问题。this指针指向被调用的成员函数所属的对象。
     *
     * this指针是隐含在每一个非静态成员函数内的一种指针。this指针不需要定义,可直接使用。
     *
     * this指针的用途:
     *  (1)当形参和成员变量相同时,可用this指针来区分,否则编译器不会认为形参是成员变量;
     *  (2)在类的非静态成员函数中返回对象本身,可用`return *this`。
     *
     * C++中空指针可以访问成员函数,但是需要注意有没有用到this指针。若用到了this指针,需要加以判断保证代码的健壮性。
     *
     * 常函数(`void showPerson() const {}`)
     *  - const修饰的成员函数叫做常函数;
     *  - 常函数内不可以修改成员属性;
     *  - 成员属性声明时加关键字mutable后,在常函数中依然可以更改;
     *
     * 常对象(`const Person p;`):声明对象前加const称该对象为常对象,常对象只能调用常函数。
     *
     * 在非常函数中(也即一般类函数),this指针是一个指针常量(Person * const this),即其指向不能更改,但是其值可以更改。反向思考:this指向的是一个实例类,如果这个指针指向了另一个类,
     * 就变成了一个类实例调用函数,其实操作的是别的实例,这种做法是不允许的,所以this指针指向不能更改。而我们可以在非常函数内给类属性赋值,这相
     * 当于改变了this指向的某个属性的值,这是很普遍的操作,是被允许的。
     * 而对于常数函数(`const Person * const this`),this指针则是变成了双const 修饰,它既不能更改指向也不能更改其指向的值了(mutable成员属性除外)。
     *
     *
     *  友元
     * 有时候,我们想让类的一些私有属性可以被其他类或函数访问到,这时候就可以使用友元。友元的目的就是让一个函数或者类访问另一个类中的私有成员。
     * 关键字为 friend
     *
     * 友元的实现有三种:
     *  (1)全局函数做友元;
     *  (2)类做友元, 友元类(其中的函数)便可以访问其私有成员了;
     *  (3)成员函数做友元。
    

    浅拷贝与深拷贝说明代码:

    //
    // Created by shexuan on 2021/1/4.
    //
    
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Person{
    public:
        // 默认(无参)构造函数
        Person(){
            cout << "默认构造函数" << endl;
        }
    
        // 有参构造函数
        Person(int age, int height){
            m_age = age;
            m_height = new int(height);
            cout << "有参构造函数" << endl;
        }
    
    //    // 拷贝构造函数1——浅拷贝
    //    Person(const Person &p){
    //        m_age = p.m_age;
    //        m_height = p.m_height;
    //    }
    
        // 拷贝构造函数1——深拷贝拷贝,重新开辟堆空间来存放数据,而不是直接复制内存地址
        Person(const Person &p){
            m_age = p.m_age;
            m_height = new int(*p.m_height);
        }
    
        //析构函数
        ~Person(){
            // 由于在堆中开辟了内存,堆中的内存需要手动释放
            if (m_height != NULL){
                delete m_height;
                m_height = NULL;
            }
            cout << "析构函数" << endl;
        }
    
        int m_age;
        int *m_height;
    };
    
    int main(){
        Person p1(18, 170);
        cout << "年龄:" << p1.m_age << " 身高(地址):" << p1.m_height << " 身高值:" << *p1.m_height << endl;
        Person p2(p1);
        cout << "年龄:" << p2.m_age << " 身高(地址):" << p2.m_height << " 身高值:" << *p2.m_height << endl;
    
    }
    

    输出:

    有参构造函数
    年龄:18 身高(地址):0x7fa417d00000 身高值:170
    年龄:18 身高(地址):0x7fa417d00010 身高值:170
    析构函数
    析构函数
    

    相关文章

      网友评论

          本文标题:黑马C++视频笔记《封装、对象属性》

          本文链接:https://www.haomeiwen.com/subject/unppoktx.html