C++面向对象(类和对象)—— 对象模型 和 this指针(封装、继承、多态)

微软技术分享 微软全球最有价值专家
全栈领域优质创作者
博客专家认证
2025-06-07 08:05:02

文章目录

三、C++对象模型和this指针

3.1成员变量和成员函数分开存储

  • C++中,类内的成员变量成员函数分开存储
  • 只有非静态成员变量才属于类的对象上

3.1.1成员变量和成员函数分开存储

3.1.1.1空对象的大小 - 面试真题

class Person {
public:

};
void test(){
    Person p;

    cout << "sizeof(p) == " << sizeof(p) << endl;
}

https://i-blog.csdnimg.cn/direct/65f84feb2a4247f8b393ceacaaea63f8.png

  • **空对象( p )**占用的内存空间是: 1。
  • C++编译器会给每个空对象分配一个字节的空间,为了 ‘区分’ 空对象所占的内存空间位置,所以让每一个(空)对象都有独属于自己的内存空间,

3.1.1.2类对象的大小关系

  • 类对象 ----> 类的实例化对象
  • 验证:非静态成员变量,属于类的对象
class Person {
public:

    int m_A;//非静态成员变量
};
void test(){
    Person p;

    cout << "sizeof(p) == " << sizeof(p) << endl;
}

https://i-blog.csdnimg.cn/direct/95e164f8e2bd436dab179b59dc97e35f.png


  • 验证:静态成员变量,不属于类的对象
class Person {
public:

    int m_A;//非静态成员变量
    static int m_B;
};
int Person::m_B = 0;//类内声明,类外初始化

void test(){
    Person p;

    cout << "sizeof(p) == " << sizeof(p) << endl;
}

https://i-blog.csdnimg.cn/direct/aad658c265894d7590cbb9f7a4e74b87.png


  • 验证:非静态成员函数,不属于类的对象
class Person {
public:

    int m_A;//非静态成员变量
    static int m_B;
    void func(){};
};
int Person::m_B = 0;//类内声明,类外初始化

void test(){
    Person p;

    cout << "sizeof(p) == " << sizeof(p) << endl;
}

https://i-blog.csdnimg.cn/direct/04eb846aada74b27b96dfcb4d0d6777e.png


  • 验证静态成员函数,不属于类的对象
class Person {
public:

    int m_A;//非静态成员变量
    static int m_B;//静态成员变量
    void func(){};//非静态成员函数
    static void func2(){};//静态成员函数
};
int Person::m_B = 0;//类内声明,类外初始化

void test(){
    Person p;

    cout << "sizeof(p) == " << sizeof(p) << endl;
}

https://i-blog.csdnimg.cn/direct/bb71c2adef5a44408c2fe0f6972c5b25.png

  • 以上我们便的出了成员变量与成员函数分开存储的结论。

3.2this 指针的概念

  • 通过3.1成员变量和成员函数分开存储 我们知道在C++成员变量成员函数是分开存储的
  • 每一个非静态存储的成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码,那么这一问题是:这一块代码是如何区分哪个对象调用自己呢?
  • C++通过提供特殊的对象指针 “this指针”,解决上述问题。 this指针****指向被调用的成员函数 所属的对象。
  • 另外 this指针本质: 常量指针(指针的指向不可修改),因此 this指针也是不可修改指针的指向
  • this指针指向的值是可修改的
//假设在类中存在一个函数(成员函数)
void func(){ }

//其中在类外有很多实例化的对象,如p1,p2,p3...,都在调用函数 func() (其中func()只有一份),那么在函数体中如何区分是p1在调用 func(),还是p2在调用func()呢?...该怎么区分是要修改p1的属性?还是修改p2的属性?...
    
//在这里,我们就使用到了thishi指针的概念了
//this指针指向被调用的成员函数的所属对象,比如p1调用成员函数,那么this->p1;
  • this指针是隐含在每一个非静态成员函数内的一种指针, this指针不需要定义,直接使用即可。

3.2.1this指针的用途
  • 形参成员变量同名时,可以用 this指针指针来区分
  • 在类的非静态成员函数返回对象本身,可以使用return* this

3.2.1.1解决名称冲突

https://i-blog.csdnimg.cn/direct/4739a3fcc98341beb3ae4483ab0d95e9.png

  • 输出结果:

https://i-blog.csdnimg.cn/direct/96c4eecc0b8d47b581cd3cb5d9b72530.png

  • 当我们将鼠标放到 形参age 上就会发现 ( 阴影部分1) ,在Person 构造函数中的age都是同一份数据,但却与成员变量 age 无关。

https://i-blog.csdnimg.cn/direct/88c8a8bcc27843d09bb035d13881e01b.png


  • 因此就需要使用 this指针 做规范。

https://i-blog.csdnimg.cn/direct/f137fafe75a248d3a640ee816ee3fed2.png

https://i-blog.csdnimg.cn/direct/3260afea89eb4dc9add887ef5510dcf2.png

  • 此时就有了一个明显区分。

https://i-blog.csdnimg.cn/direct/20f3189f137b40a8825c6162889ab162.png


  • 总结: this 指针指向 被调用的成员函数( 有参构造函数 ) 所属 的对象。
  • p1(18)^(调用)^ --->Person( int age )^(有参构造函数)^,相当于
  • this指针^指向^ --->p1(被调用的成员函数 所属 的对象)

3.2.1.2返回对象本身 — *this

  • 为 p2 的属性增值
#include<iostream>
using namespace std;

class Person {
public:
    Person(int age) {
        this->age = age;
    }
    //返回值为 void
    void PersonAddAge(Person& p) {
        this->age += p.age;
              ^        ^
 (this指针指向)p2      p1
    }
    int age;
};
void test() {
    Person p1(18);
    Person p2(20);

    p2.PersonAddAge(p1);//为 p2 属性增值
    cout << "p2的年龄为: " << p2.age << endl;
    
//当我们持续为 p2 增值是无法实现的
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);--->false
}
int main(){    test();return 0; }
  • 因为 PersonAddAge( )函数 首次调用结束后类型是 void,但p1是 Person 类型,因此无法继续链式调用PersonAddAge( )。因此我们需要让PersonAddAge( )的返回类型为 Person 才可以实现链式调用的可能。

3.2.1.3链式编程思想 - 链式调用

class Person {
public:
    Person(int age) {
        this->age = age;
    }
    //Person& 表示返回一个Person的引用,使用返回this指针返回 成员对象age的本体需要使用引用的方式
    Person& PersonAddAge(Person& p) {

        this->age += p.age;//将参数对象的属性加到当前对象的属性上
        return* this; //返回当前对象的引用,支持链式调用

        //因为this 是一个指向p2的指针,所以*this 指向的就是这个对象的本体
    }
    int age;
};
void test() {
    Person p1(18);
    Person p2(20);
    
    //链式编程思想 - 链式调用
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
    cout << "p2的年龄为: " << p2.age << endl;
}
int main(){test(); return 0;}

https://i-blog.csdnimg.cn/direct/72a7f1d0683e45d99bf6fae5d8003412.png


3.2.1.4引用置于链式调用的关键地位

返回 Person 的引用
Person& PersonAddAge(Person& p)
    
               返回 Person 的值
更改为  ====>>  Person PersonAddAge(Person& p)
输出结果???

https://i-blog.csdnimg.cn/direct/ea1a3c90cede4944a8d229022ae3a9d5.png

  • 核心过程:
  • p2.PersonAddAge(p1)第一次被调用后,返回的并不是p2 的本体!而是p2‘ == 38!(按照本体p2,调用拷贝构造函数创建了一个新的数据p2’!)—— 拷贝函数调用时机 ——以值的方式返回,调用拷贝构造函数,拷贝复制出一份新的数据p2’

https://i-blog.csdnimg.cn/direct/eec2141485604918af7a9b0048946a96.png

  • Person 与 this指针何时发生变化?

https://i-blog.csdnimg.cn/direct/4ffc053fb1f848fcbcc10855058b475b.png

https://i-blog.csdnimg.cn/direct/514c7e3624bf424a9e6bdf96d6a6a58b.png

  • 无论调用几次结果都是一样的。

https://i-blog.csdnimg.cn/direct/739cb73d3c914d0496d7a68eabadd962.png


3.3空指针访问成员函数

  • C++ 空指针也是可以调用成员函数,但也要注意是否使用到 this指针
  • 如果用到this指针,需要加入判断保证代码健壮性

3.3.1读取访问权限冲突
//使用空指针调用成员函数
class Person {
public:
    void showClassName() {
        cout << "This is Person Class!" << endl;
    }

    void showPersonAge()
    {
        cout << "This Person age is: " << age << endl;
    }

    int age;
};
void test() {
    Person* p = NULL;//指针数据类型是Person*,指向空

    p->showClassName();--->true
    p->showPersonAge();--->false
}

https://i-blog.csdnimg.cn/direct/755abb0e3a794e9ca812ff411dc62dcd.png

  • 代码错误分析:

(this->)age,告诉用户这是当前对象的属性(成员变量)

void showPersonAge(){
在我们类中函数的属性,其实都默认自带 (this->)age,告诉用户这是当前对象的属性(成员变量)
        cout << "This Person age is: " << age << endl;
    }

但问题就出在:Person* p = NULL;

在this指针的概念中,我们有详细说明this指针的指向实质是:被调用的成员函数 所属的对象

因此this->p(p == NULL),导致this->NULL,而并未指向一个确切的数据;

所以this 所指向的并不是一个实体(因为p为NULL,因此实例对象p,可视为不存在,因此Person并没有实例出任何对象,所以更不可能通过一个空值(this->p)调用类中的成员变量!)

  • 解决方法:
class Person {
public:
    void showClassName() {
        cout << "This is Person Class!" << endl;
    }

    void showPersonAge()
    {
        //设置判断条件,保证代码的健壮性
        if(this == NULL)
        {
            return;
        }
        cout << "This Person age is: " << age << endl;
    }

    int age;
};
void test() {
    Person* p = NULL;//指针数据类型是Person*,指向空

    p->showClassName();--->true
    p->showPersonAge();--->false
}

3.4const修饰成员函数

  • 常函数
  • 成员函数形参列表后加const,我们称这个函数为常函数
  • 常函数内不可修改成员属性 - 只读状态
  • 成员函数声明时加关键字mutable(adj.可变的)后,在常函数中依然可以修改
  • 成员函数形参列表后加入const本质上修饰的就是 this指针****(常量指针)
  • 因此使用const修饰 this指针:**const Person* const this;常量指针常量)**
class Person{
public:
    void showPerson()
    const{ (this->)a = 100;//--->false
           (this->)b = 200;//--->true
         }//--->const Person* const this;
    int a;
    mutable int b;//使 b 变为特殊值,在常函数下可修改
}
  • 常对象
  • 类对象前加const称该对象为常对象
  • 常对象只能调用常函数
void test(){
    const Person p;//在对象前加const,变为常对象
    p.a = 20;//--->false
    p.b = 30;//--->true,b 为特殊值,在常对象下可修改
    
    //常对象只能调用常函数
    p.showPerson();//--->true
    p.func(普通成员函数);//--->false,
}

  • 表示同一份数据。 ↩︎

文章来源: https://blog.csdn.net/2401_87692970/article/details/146245275
版权声明: 本文为博主原创文章,遵循CC 4.0 BY-SA 知识共享协议,转载请附上原文出处链接和本声明。


...全文
47 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

5,411

社区成员

发帖
与我相关
我的任务
社区描述
微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。
windowsmicrosoft 企业社区
社区管理员
  • 山月照空舟
  • 郑子铭
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。

予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

试试用AI创作助手写篇文章吧