「地表最强」C++核心编程(六)类和对象--对象模型和this指针

环境:
编译器:CLion2021.3;操作系统:macOS Ventura 13.0.1


地表最强C++系列传送门:
「地表最强」C++核心编程(一)内存分区模型
「地表最强」C++核心编程(二)引用
「地表最强」C++核心编程(三)函数提高
「地表最强」C++核心编程(四)类和对象----封装
「地表最强」C++核心编程(五)类和对象----对象初始化和清理
「地表最强」C++核心编程(六)类和对象----对象模型和this指针
「地表最强」C++核心编程(七)类和对象----友元
「地表最强」C++核心编程(八)类和对象----运算符重载
「地表最强」C++核心编程(九)类和对象----继承
「地表最强」C++核心编程(十)类和对象----多态
「地表最强」C++核心编程(十一)文件操作

一、成员变量和成员函数分开存储

在C++中,虽然类中的成员变量和成员函数被封装在一起,但是他们是分开存储的,只有非静态成员变量才属于类的对象。
如果一个类是空的,那么他实例化的对象占用一个字节的空间,这么做是为了区分不同的空对象:

class Person {
};

void test01() {
    Person p;
    //空对象占用内存空间是1:给空对象分配一个字节的空间是为了区别空对象,每个空对象有独一无二的地址。
    cout << "sizeof p = " << sizeof(p) << endl;//1
}

分开存储:

class Person {
    int mA;//非静态成员变量,属于类的对象上
    static int mB;//静态成员变量,不属于类的对象上
    void func(){}//非静态成员函数,不属于类的对象上
    void func2(){}//静态成员函数,不属于类的对象上

};
int Person::mB = 10;

void test02() {
    Person p;
    //非空时,只计算非静态成员变量的大小,不计算静态成员变量和任何成员函数,因为成员变量和成员函数是分开存储的。
    cout << "sizeof p = " << sizeof(p) << endl;//4,是mA的大小,其他的都不属于这个对象p
}

二、this指针概念

由于C++中成员变量和成员函数是分开存储的,每一个非静态成员函数只会产生一份函数实例,也就是说多个同类型的对象会共用这一块代码,那么这一块代码是如何区分哪个对象调用自己的呢?c++通过提供特殊的对象指针,this指针,解决此问题,this指针的本质是一个指针常量,其指向不可修改。

this指针有如下特点:

  • this指针指向当前调用此函数的对象
  • this指针是隐含每一个非静态成员函数内的一种指针
  • this指针不需要定义,直接使用即可

this指针的用途:
1.当形参和成员变量同名时,可用this指针来区分

class Person
{
public:
    int age;

    Person(int age)
    {
//        age = age;//这两个age都是形参的age,并没有改变成员变量的age。 两种解决方法:1利用this  2改名成员变量,防止冲突
        this->age = age;//this指针指向当前调用此函数的对象
    }
}

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

class Person
{
public:
    int age;

    Person& PersonAddPerson(const Person &p){//注意返回值是Person&而非Person
        this->age += p.age;
        //返回对象本身用*this,this指向对象,*this就是该对象
        return *this;
    }
};

void test01()
{
    Person p1(10);
    cout << "p1.age = " << p1.age << endl;

    Person p2(10);
    p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
//若返回值为Person,经过p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);后p2.age的值还是20,
//因为值传递会临时开辟空间而不是一直操作p2,这就导致了上述操作只有第一次调用是在操作p2,后边两次调用age确实增加了,但这不是p2的age,而是临时对象的age
    cout << "p2.age = " << p2.age << endl;//40,若返回值类型Person则为20
}

三、空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性。

class Person {
public:
    int mAge;

    void ShowClassName() {
        cout << "我是Person类!" << endl;
    }

    void ShowPerson() {
        if (this == NULL) {//防止空指针
            return;
        }
        cout << mAge << endl;//这里的mAge会被解析成this.mAge,用到了this指针,此时this不可以为空
    }
};

void test01() {
    Person *p = NULL;
    p->ShowClassName(); //空指针,可以调用成员函数
    p->ShowPerson();  //但是如果成员函数中用到了this指针,就不可以了
}

四、常函数和常对象

4.1 常函数

成员函数加const后我们称为这个函数为常函数,这个const实际上是用来修饰this指针的,由于this本质是指针常量Type* const pointer;不能修改其指向,在前面加上const后就是const Type* const pointer;此时其内容也无法修改。因此常函数内不可以修改成员属性,但是成员属性声明时加关键字mutable后,在常函数中依然可以修改。

class Person {
public:
    int m_A;
    mutable int m_B; //即使在常函数中也可修改,常对象也可以修改

    Person() {
        m_A = 0;
        m_B = 0;
    }

    //this指针的本质是一个指针常量,指针的指向不可修改
    void ShowPerson() const {//此处的const修饰的是this指针,让其内容也不可以修改,这就是常函数,也就是:const Type* const pointer;
        //this = NULL; //err,Person* const this;不能修改指针的指向
        //this->mA = 100; //err,若没有const修饰,则ok

        //const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
        this->m_B = 100;//ok,因为有mutable
    }
    void MyFunc()  {
        m_A = 10000;
    }
};
/*区别两个指针:
	Person* const this;不能修改指针的指向,但指向的内容可以修改
	const Person* const this;不能修改指针的指向,也不能修改指向的内容
*/

4.2 常对象

声明对象前加const称该对象为常对象,常对象也不能修改成员变量的值,由于普通的成员函数可以更改成员变量的值,这与常对象不能更改成员变量的值冲突,因此常对象只能调用常函数,但是成员变量用mutable修饰的话,则可以被常对象更改。

void test() {
    const Person person; //常量对象
    cout << person.m_A << endl;
//    person.m_A = 100; //err,常对象不能修改成员变量的值,但是可以访问
    person.m_B = 100; //ok,常对象可以修改mutable修饰成员变量

    //常对象只能访问常函数
    person.ShowPerson();//常对象只能调用const的函数
//    person.MyFunc(); //err,常对象不能调用普通成员函数,因为普通成员函数可以修改属性,与常对象冲突

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值