C嘎嘎~~[类 中篇]

简介: 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\n", sizeof(PersonInfor)); // 56
  printf("对象的大小 = %d\n", sizeof(p)); // 56
}

得到的知识点:

1.根据结构体的所学知识, 我们不难知道成员变量的大小就是 56, 而对象 和 类的大小也是56, 验证了成员函数不是包含在对象中的.

2.sizeof(对象) = sizeof(类)

3.以 arr.Push( ) 为例子: 调用这个函数, 是不会在对象里面去寻找的, 因为成员函数没在对象里.


疑惑点:

1.成员函数到底在哪里??

2.上面的代码, 我这样调用可以吗? PersonInfor :: PrintPersonInfor()??

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


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


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


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


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


回答:

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

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

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

  错误原因: 其实这里有个 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)


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


补充:


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


88333d77771740b1b5ef4827c188cf39.png


8.2this指针的特性

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


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

3359d3de4fee42af8c808bef99b1fcf3.png


3.this指针本质是成员函数的形参, 当对象调用成员函数时, 将对象的地址作为实参传递给this形参, 所以对象是不存储this指针的.

4.切记: 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;
}

4820ec7769e145649ebe3453a22b14d8.png

相关文章
|
6月前
|
设计模式
二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮
二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮
|
存储 编译器 C++
【C++】类和对象(中篇)
【C++】类和对象(中篇)
52 0
|
5月前
|
编译器 C++
c++primer plus 6 读书笔记 第十章 对象和类
c++primer plus 6 读书笔记 第十章 对象和类
|
6月前
|
编译器 C++
【C++】一文全解四种经典 [ 特殊类 ]的设计
【C++】一文全解四种经典 [ 特殊类 ]的设计
|
6月前
|
存储 编译器 C++
类和对象(中篇)
类和对象(中篇)
45 1
|
6月前
|
存储 Java
java反射——设计框架的灵魂
java反射——设计框架的灵魂
|
6月前
|
设计模式
二十三种设计模式全面解析-建造者模式:构建完美对象的秘密武器
二十三种设计模式全面解析-建造者模式:构建完美对象的秘密武器
|
6月前
|
设计模式 Java 数据库
二十三种设计模式全面解析-单例设计模式:解密全局独一无二的实例创造者
二十三种设计模式全面解析-单例设计模式:解密全局独一无二的实例创造者
|
6月前
|
Java
Java反射的详细解析之三
面试题: 你觉得反射好不好?好,有两个方向 第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。
42 0
|
存储 编译器 C++
C嘎嘎~~ [类 下篇]
C嘎嘎~~ [类 下篇]