• C嘎嘎~~[类 中篇]


    6.类的实例化

    什么叫类的 实例化??

    首先, 我们应该关注这个"实" — 实际存在的, 它的反义词是 “虚” — 不存在的. ==> 类中的成员变量是虚的(相当于声明), 在类外面创建的对象是实际存在的(相当于定义).

    用类类型创建对象的过程, 成为类的实例化.

    1. 类是对对象进行描述的, 是一种模型一样的东西, 限定了类有哪些成员. 类是没有分配实际的内存空间,变相地说明了类中的成员变量是一种声明哦

      class PersonInfor
      {
      public:
      	void PrintPersonInfor()
      	{
      		cout << _name << endl;
      		cout << _adress << endl;
      		cout << age << endl;
      	}
      
      private:
      	char _name[20];
      	char _adress[30];
      	int _age;
      
      };
      
      int main()
      {
      	PersonInfor::_name; // error C2597: 对非静态成员“PersonInfor::_name”的非法引用
                              // message : 参见“PersonInfor::_name”的声明
      	PersonInfor::_adress; // error C2597: 对非静态成员“PersonInfor::_adress”的非法引用
                                 // message : 参见“PersonInfor::_adress”的声明
      	PersonInfor::_age; // error C2597: 对非静态成员“PersonInfor::_age”的非法引用
                              // message : 参见“PersonInfor::_age”的声明
      
      }
      
      • 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

      肯定有些老铁会尝试上面的做法, 很明显是错误的!!!

      1. 这些老铁应该是这样想的: PersonInfor 是一个类名, 那么就存在类域, 我就可以用域作用限定符 (😃 来访问类中的成员变量~~

      2. 错误原因: 其实这种说法就是错误的~ ,我们想一想什么叫做访问? 访问的东西肯定要有内存空间吧, 要不访问个啥呢~~

        其实,很多词语就暗着内存空间: 访问, 初始化, 有可能你现在看这些一点反应都没有, 学到后面这些用途大着嘞.

    2. 一个类可以实例化多个对象, 这点应该不用多说(类是一种变量类型, 相当于我们常见的 int, double, char…). 但是实例化出的对象 占用的物理空间只存储了成员变量, 即成员函数是不在对象中的.

      class PersonInfor
      {
      public:
      	void PrintPersonInfor()
      	{
      		cout << _name << endl;
      		cout << _adress << endl;
      		cout << age << endl;
      	}
      
      private:
      	char _name[20];
      	char _adress[30];
      	int _age;
      
      };
      
      int main()
      {
      	PersonInfor p;
      	printf("类的大小 = %d\n", sizeof(PersonInfor)); // 56
      	printf("对象的大小 = %d\n", sizeof(p)); // 56
      }
      
      
      • 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. 根据结构体的所学知识, 我们不难知道成员变量的大小就是 56, 而对象 和 类的大小也是56, 验证了成员函数不是包含在对象中的.
        2. sizeof(对象) = sizeof(类)
        3. 以 arr.Push( ) 为例子: 调用这个函数, 是不会在对象里面去寻找的, 因为成员函数没在对象里.
      • 疑惑点:
        1. 成员函数到底在哪里??
        2. 上面的代码, 我这样调用可以吗? PersonInfor :: PrintPersonInfor()??

    先对类的实例化打一个形象地比喻:

    声明就好比是一张房屋的设计图纸, 成员变量就是那图纸上的一个个房间, 成员函数先靠边~~

    设计图纸终归也就是一张纸而已, 里面的设计的东西再怎么好也是不存在的.

    而我们的对象就是把这个图纸实际化 ==> 真真切切地造一个房子出来. 房子是按照图纸建造的, 里面的东西肯定只有也只能有这些房间(成员变量) ==> 类是对对象进行描述的, 是一种模型一样的东西, 限定了类有哪些成员.

    这时候, 我们想到那娱乐设施呢, 就比如篮球场~, 篮球场是一家建一个 还是 一个小区建一个好一些?? 答案不言而喻, 一个小区建一个, 不仅经济实惠, 还能融洽小区关系呢~~ 成员函数就好比是这篮球场, 它是不会存放在一个个对象中的(占空间), 而是存放在一个公共位置, 大家都可以使用哦.


    回答:

    1. 成员函数的存放位置在一个公共位置, 同一个类型的对象都可以调用

    2. PersonInfor :: PrintPersonInfor() 是不可以的:

      1. 老铁是这样想的: 反正成员函数是不在对象中的, 那我就可以不用对象去访问它了吧

      2. 错误原因: 其实这里有个 this指针,这个后面会讲~


        或者大家有么有想过下面的几个问题:

        1. 每个对象调用的成员函数都是一样的吗?
        2. 如果是一样的, 那么为什么多个对象调用有多个不同的结果??

    7.类对象模型

    一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐

    注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象

    • 内存对齐的规则:
    1. 第一个成员在与结构体偏移量为0的地址处
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为8
    3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是 所有最大对齐数(含嵌套结构体的对齐数)的整数倍
    • 小问题:
    1. 结构体怎么对齐? 为什么要进行内存对齐?
    2. 如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐?
    3. 什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景?

    8.this指针

    通过前面的学习, 相信大家对this指针充满了好奇心
    this指针对我们以后的学习有着无与伦比的作用

    8.1this指针是什么

    class Data
    {
    public:
    	void Init(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Print()
    	{
    		cout << _year << " " << _month << " " << _day << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	// 对象实例化
    	Data d1;
    	Data d2;
    
    	// 调用函数
    	d1.Init(2023, 12, 1);
    	d2.Init(2023, 12, 12);
    	d1.Print();
    	d2.Print();
    
    }
    
    • 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

    this指针, C++编译器给每个"非静态的成员函数" 增加了一个隐藏的指针参数, 让该指针指向当前对象(函数运行时调用该函数的对象), 在函数体中所有"成员变量"的操作, 都是通过该指针去访问. 只不过所有的操作对用户是透明的, 即不需要用户来传递, 编译器自己完成.

    上面, 我们留了一个问题: “如果每个对象调用的成员函数是一样的, 那么为什么多个对象调用有多个不同的结果??”, 这个时候就能过给大家解答了:

    让该指针指向当前对象(函数运行时调用该函数的对象), 在函数体中所有"成员变量"的操作, 都是通过该指针去访问 ==>

    d1.Init() ==> d1.Init(&d1), d1.Print( ) ==> d1.Print(&d1)

    这样大家就明白了: 为什么调用的同一个函数, 为什么结果不一样~~


    补充:

    不同对象调用的成员函数是相同的原因:


    8.2this指针的特性

    1. this指针的原型是:对象类型* const this, 即在成员函数中, 不能给this指针赋值, 不能改变this指针.

    2. this指针不能在形参 和 实参显示传参, 但是能够在成员函数中使用(这个以后还有大用途)

      1. this指针本质是成员函数的形参, 当对象调用成员函数时, 将对象的地址作为实参传递给this形参, 所以对象是不存储this指针的.
      2. 切记: this指针是成员函数的"第一个隐含的指针形参", 用户不需要也不能传递

      • 解决上面的两个疑问:

        1. “PersonInfor :: PrintPersonInfor() 是不可以的”

          这个是因为成员函数不知道该传递啥~, :: 是访问的意思哎, 并没有传递对象的地址哎

        2. 那有些老铁就会说: PersonInfor :: PrintPersonInfor(对象的地址) , 这样行吧??

          你的这些小聪明, 我都是看在眼里的哈哈~~

          这里虽然传递了对象的地址, 但是你忽略了一点:this指针在形参和实参是不显示的哎~~

      • 小问题:

        1. this指针存放在哪里?

          因为this指针是成员函数的形参, 所以this指针是跟普通函数一样存放在函数调用的栈区的, 函数调用结束就销毁了

        2. this指针可以为空吗?

          分情况:

          1. 如果this指针传过去没有访问this所指向的内容, 这样是可以的
          2. 如果this指针传过去有访问this所指向的内容, 这样会报运行错误

          通过下面的俩个例子来解释一下:

          class test
          {
          public:
          	void test1()
          	{
          		cout << "test1(_)" << endl; // 并没有访问this指针
          	}
          
          	void test2()
          	{
          		cout << _a << endl; // 访问了this指针
          	}
          
          private:
          	int _a;
          };
          
          int main()
          {
          	test* a = nullptr;
          	a->test1(); // test1(_)
          	a->test2(); // error
          
          	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

          在这里插入图片描述

  • 相关阅读:
    javaweb电脑销售管理系统的设计
    某农信企业自主创新自动化安全基线检测平台建设实践
    __attribute__((visibility(“default“)))含义
    Springboot实现RBAC权限校验
    四川大学874考研真题00-23
    Kubeadm 部署 k8s 集群
    子查询及分组查询
    【附源码】Python计算机毕业设计苹果酒店住房管理
    MyBatis快速上手与知识点总结
    软考高级系统架构设计师系列之:数学与经济管理
  • 原文地址:https://blog.csdn.net/qq_67549203/article/details/130408531