【C++】初识类和对象

一给  金牌会员 | 2024-8-3 10:09:54 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 964|帖子 964|积分 2892


本篇先容一下C++的自界说类型,类和对象。
1.类的界说

1.1 类界说格式

   class 为界说类的关键字,Stack为类的名字,类名任意取,{}中为类的主体,类界说结束时背面的分号不可省略。类主体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中函数称为类的方法或成员函数。
   与C语言的布局体的界说相似,第一个不同就是类成员还可以是函数,C语言的布局体内里没有函数。比如我们界说一个栈的类。
  1. class Stack
  2. {
  3.         void Push(int x)  //成员函数(类的方法)
  4.         {
  5.                 //...
  6.         }
  7.         void Pop()
  8.         {
  9.                 //...
  10.         }
  11.         int top()
  12.         {
  13.                 //...
  14.         }
  15.         int* _a;           //成员变量(类的属性)
  16.     int _top;
  17.     int _capacity;
  18. };
复制代码
为了区分成员变量,一样平常风俗在成员变量上加一个特殊标识,如_或者m开头,有的也把_加在成员变量名背面,像int* a_;  ,但这并不是强制的。
C++中也可以用struct界说类,C++兼容C中的struct用法,同时struct升级成了类,显着变化是C++的struct中可以界说函数,像下面的日期类,class可以是struct。一样平常情况下照旧推荐用class界说类。
  1. struct Date
  2. {
  3. public:
  4.         void Init(int year, int month, int day)
  5.         {
  6.                 _year = year;
  7.                 _month = month;
  8.                 _day = day;
  9.         }
  10. private:
  11.         int _year;
  12.         int _month;
  13.         int _day;
  14. };
复制代码
  在类内里直接界说的函数默认就是内联函数,但是声明和界说分离就不是内联了,比如在类内里声明,在类外面界说。
  
1.2访问限定符

C++一种实现封装的方式,用类将对象的属性(变量)和方法(函数)联合在一起,让给对象更完善,通过访问权限选择性的将其接口提供给外部的用户使用。
   访问限定符有3个:public  private  protected。public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private目前是一样的,背面的学习中才气体现出他们的区别。
   访问权限作用域从该访问限定符出现的位置开始,到下一个访问限定符出现为止。如果背面没有访问限定符,作用域就到}即类的结束。
  1. class Stack
  2. {
  3.         void Push(int x)  //成员函数(类的方法)
  4.         {
  5.                 //...
  6.         }
  7. public:
  8.         void Pop()
  9.         {
  10.                 //...
  11.         }
  12.         int top()
  13.         {
  14.                 //...
  15.         }
  16. private:
  17.         int* _a;           //成员变量(类的属性)
  18.         int _top;
  19.         int _capacity;
  20. };
复制代码
class界说成员没有被限定符修饰时默以为private,struct默以为public。一样平常来说,成员变量都会被限制为private/protected,必要给别人使用的成员函数会被设为public。 
第二个不同是类名就是类型,可直接用类名界说对象。比如这里任意弄个布局体来对比一下,类照旧用前面那个Stack类举例。
  1. struct S
  2. {
  3.         int a;
  4. };
复制代码
  1. struct S s; //C语言结构体定义结构体变量
  2. Stack st;   //类定义对象
复制代码
类不必要写成class Stack st; 直接Stack st;  C语言的布局体就不能丢掉struct。
由于C++兼容C语言,传统的struct在C++可以用,而struct在C++中又升级成了类,所以在C++中,下面两种写法都可以
  1. struct S s;
  2. S s;
复制代码
  1. struct ListNode  //C语言链表节点的表示
  2. {
  3.         int val;
  4.         struct ListNode* next;
  5. };
  6. struct ListNode  //C++链表节点的表示
  7. {
  8.         int val;
  9.         ListNode* next;
  10. };
复制代码
C语言的表示在C++也能用,C++就有两种表示方法 

当我们访问类成员时,跟布局体一样,用点(.)访问。

当我们引出成员变量时发现只有public限制的可以访问,Push没有访问限定符限制,默以为私有,成员变量也是私有。
这里访问限定符,可以重复设置,比如我们想把Push设为公有,可重复使用public,但是一样平常放在一起,这里只是说明一下可以重复。
  1. class Stack
  2. {
  3. public:
  4.         void Push(int x)  //成员函数(类的方法)
  5.         {
  6.                 //...
  7.         }
  8. public:
  9.         void Pop()
  10.         {
  11.                 //...
  12.         }
  13.         int top()
  14.         {
  15.                 //...
  16.         }
  17. private:
  18.         int* _a;           //成员变量(类的属性)
  19.         int _top;
  20.         int _capacity;
  21. };
复制代码

1.3 类域

类界说了一个新的作用域,类的所有成员都在类的作用域中,在类外界说成员时,必要用::作用域操作符指明成员属于哪个类域。
  1. class Stack
  2. {
  3. public:
  4.         void Push(int x);  //类内:成员函数的声明
  5. private:
  6.         int* _a;      //成员变量     
  7.         int _top;
  8.         int _capacity;
  9. };
  10. void Stack::Push(int x) //类外:函数的定义
  11. {
  12.         //...
  13. }
复制代码

2.实例化

2.1 实例化概念

   • 用类类型在物理内存中创建对象的过程,称为类实例化出对象。
• 类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明没有分配空间,用类实例化出对象时,才会分配空间。
• 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量
  打个比方:类实例化出对象就像实际中使用修建计划图制作出房子,类就像是计划图,计划图规划了有多少个房间,房间巨细功能等,但是并没有实体的修建存在,也不能住人,用计划图修建出房子,房子才气住人。

 同样类就像计划图一样,不能存储数据,实例化出的对象分配物理内存存储数据。

2.2 对象巨细

分析一下类对象中哪些成员呢?类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?
   首先函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year / _month / _day 存储各自的数据,但是d1和d2的成员函数Init /Print指针却是一样的,存储在对象中就浪费了。如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。这里必要再额外说一下,其实函数指针是不必要存储的,函数指针是一个地址,调用函数被编译成汇编指令[call地址],其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就必要存储函数地址,这个我们以后会解说。
  上面一大段的意思就是对象的巨细不盘算成员函数,只看成员变量,成员函数会存在一个公共区域。
对象中的成员变量在内存中的存储和C语言时布局体在内存中的存储规则一模一样,要内存对齐,布局体内存对齐详解在【C语言】布局体详解-CSDN博客 ,内存对齐规则以及为什么要内存对齐都在这篇写过,不知道的可以先看布局体这篇。
我们照旧举个例子回首一下。下面这个A类对象有多大?
  1. class A
  2. {
  3. public:
  4.         void Ptint()
  5.         {
  6.                 cout << "void Ptint()" << endl;
  7.         }
  8. private:
  9.         char _ch;
  10.         int _i;
  11. };
复制代码
应该是8个字节 ,详细盘算如下

 我们再来看两个,类B内里只有成员函数,类C内里没有成员,它们巨细是多少呢?0吗?
  1. class B
  2. {
  3. public:
  4.         void Ptint()
  5.         {
  6.                 cout << "void Ptint()" << endl;
  7.         }
  8. };
  9. class C
  10. {
  11. };
复制代码
 我们用类B实例化一个对象b,类C实例化一个对象c,看看它们的巨细。
  1. B b; //实例化对象
  2. C c;
  3. cout << sizeof(b) << endl;
  4. cout << sizeof(c) << endl;
复制代码

我们可以看到类B和类C对象的巨细是1个字节,为什么不是0?由于如果一个字节也不给,怎么表示对象存在过呢?所以就给1个字节,纯粹是为了占位,表示对象存在。

3. this指针

我们先看下面这个时间类
  1. class Date
  2. {
  3. public:
  4.         void Init(int year, int month, int day)
  5.         {
  6.                 _year = year;
  7.                 _month = month;
  8.                 _day = day;
  9.         }
  10.         void Print()
  11.         {
  12.                 cout << _year << "." << _month << "." << _day << endl;
  13.         }
  14. private:
  15.         int _year;
  16.         int _month;
  17.         int _day;
  18. };
复制代码
我如今实例化两个对象d1和d2
  1. Date d1;
  2. Date d2;
复制代码
 用这两个对象像下面这样引出内里的函数
  1. d1.Init(2024, 8, 2);
  2. d1.Print();
  3. d2.Init(2024, 8, 3);
  4. d2.Print();
复制代码
运行,看结果

思索一下,d1和d2明明执行的是同一个函数,结果为什么不一样? 函数体中没有关于不同对象的区分,那么当d1调用Init函数和Print函数的时候该函数是怎样知道访问d1对象照旧d2对象?

这里就先容一个C++给的一个隐含的this指针解决问题。
编译器编译后,类的成员函数默认都会在形参的第一个位置增加一个当前类类型的指针,叫this指针。所以Init的第一个形参并不是year,Print函数也并不是没有参数,前面的Print函数和Init函数实际上是像下面这样。
  1. void Init(Date* const this, int year, int month, int day)
  2. {
  3.         //...
  4. }
  5. void Print(Date* const this)
  6. {
  7.         //...
  8. }
复制代码
 所以传参的时候也会传相应的地址过去。
  1. //d1.Init(&d1, 2024, 8, 2);
  2.   d1.Init(2024, 8, 2);
  3. //d1.Print(&d1)
  4.   d1.Print();
  5. //d2.Init(&d2, 2024, 8, 3);
  6.   d2.Init(2024, 8, 3);
  7. //d2.Print(&d2)
  8.   d2.Print();
复制代码
类的成员函数中访问成员变量,本质是通过this指针访问的。
  1. void Init(int year, int month, int day)
  2. {
  3.         this->_year = year;
  4.         this->_month = month;
  5.         this->_day = day;
  6. }
  7. void Print()
  8. {
  9.         cout << this->_year << "." << this->_month << "." << this->_day << endl;
  10. }
复制代码
 C++规定不能在实参和形参的位置显示的写this指针,编译时编译器会处理,但是可以在函数体内显示使用this指针。this指针是不能修改的。
一样平常this指针存在栈区,由于this指针其实是函数的形参,形参就存放在栈区,有的也会存放在寄存器内里,vs下就是把this指针放在了寄存器里。

这次就分享到到这里,拜拜~


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

一给

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表