您现在的位置是:首页 >技术杂谈 >C嘎嘎~~[类 中篇]网站首页技术杂谈

C嘎嘎~~[类 中篇]

雨 子 2023-06-18 00:00:03
简介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. 这些老铁应该是这样想的: 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
    ", sizeof(PersonInfor)); // 56
    	printf("对象的大小 = %d
    ", sizeof(p)); // 56
    }
    
    
    • 得到的知识点:
      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();

}

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;
        }
        

        在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。