目录
前言:
这篇文章是对类和对象的初步介绍,我将用三篇文章描述类和对象,希望对大家有所帮助
什么是对象:
对象是一个实体,我们眼睛能看到的实体都是对象。
什么是类:
类是用来对实体(对象)进行描述的,如对象有什么属性,功能等。类可以看做C语言中的结构体的加强版,在C++中结构体被认为是类的一种。
class是类的关键字,stack是类的名字,{}中是类的成员,注意类定义结束时后⾯分号不能省略,类中的成员包括变量和函数。类中的变量称为类的属性或成员变量,类中的函数称为类的⽅法或 者成员函数。
- #include
- using namespace std;
- class stack//类型名
- {
- //类的成员
- //
- //函数称为类的⽅法或 者成员函数
- void add()此时为内联函数
- {
- //.......
- }
- //类的属性或成员变量
- int a = 10;
- int b = 0;
- };//后⾯分号不能省略
- //第二种,可以把声明留在类里,定义放在外面或别的文件
- ///比如类的声明在.h文件,定义在.c文件
- //.h文件
- class stack//类型名
- {
- //类的成员
- //
- //函数称为类的⽅法或 者成员函数
- public:
- void add();
- //类的属性或成员变量
- int a = 10;
- int b = 0;
- };//后⾯分号不能省略
- //.c文件
- void stack::add()
- {
- //........
- }
类的定义中需要注意的细节:
①.C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是 struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
②在类中定义的成员函数,默认为是内联函数(inline)
③为了避免函数形参和类中的成员函数名相同,为了避免混淆最好加上一些特殊标记。
④两种定义方法都可以使用,必进编译器会忽略长函数,我们可以将短的函数写在里面,长的函数写在外面
访问限定符是C++的一种封装方式,将类的属性和类的方法联系在一起,通过访问限制选择性的将一些内容给外界使用。
| public | 修饰的成员在类外可以直接被访问 |
| private | 修饰的成员在类外不能直接被访问 |
| protected | 修饰的成员在类外不能直接被访问 |
访问限定符中的细节:
①一个访问限定符的作用域,从这个访问限定符开始到下一个访问限定符结束,如果没有就到}结束。
②class当类内没有访问限定符时,默认为private;struct中默认为public。
③public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是⼀样的,这里我们还未详细讲解,以后继承章节才能体现出他们的区别。
④public在类外和类内都可以被直接访问。
- class stack
- {
- public:
- int a = 10;
- int b = 3;
- private:
- void add()
- {
- //.....
- }
- protected:
- int c = a + b;//public在类内可以直接访问
- add();//private不可以直接访问
- };
上一篇文章我们讲了C++中的域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/ 类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响 编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期。
类域是一个全新的作用域,在类外要定义时,需要使⽤ :: 作⽤域操作符指明成员属于哪个类域。
- class stack
- {
- public:
- void STinit(int n=4);
- private:
- int val;
- int top = 0;
- int capacity = 4;
- };
- void stack::STinit(int n = 4)
- {
- int* tmp = (int*)malloc(sizeof(int) * n);
- if (tmp == NULL)
- {
- perror("malloc fail!");
- }
- }
- int main()
- {
- stack st;
- st.STinit();
- }
如果说类是对对象的描述,而对象可以是眼睛看到的任何实体,那我们也可以认为对象是一个房子时,类是对房子的描述,即为图纸。在没有实体化的时候,是不占空间的。
类是可以定义多个对象的,就像一个图纸可以建很多个房子。
- class stack
- {
- public:
- void STinit(int n=4);
- private:
- int val;
- int top = 0;
- int capacity = 4;
- };
- void stack::STinit(int n = 4)
- {
- int* tmp = (int*)malloc(sizeof(int) * n);
- if (tmp == NULL)
- {
- perror("malloc fail!");
- }
- }
- int main()
- {
- stack st1;
- stack st2;
- st1.STinit();
- st2.STinit();
- }
之前我们在上面讲述了Class是C语言中struct的加强版,那么类对象的大小,是否和结构体的大小有关呢?
答案是相关的,但是也有一些不同。
①类中包含成员变量和成员函数,类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含 成员变量,那么成员函数是否包含呢?
→→⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针
→→对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量 _year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象 中就浪费了。如果⽤Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。
→→所以成员函数不在类中存储,他们在公共的代码区中存储。
这样类中就只用存储成员变量这与结构体就相同了,遵循内存对齐规则。

如果这个类是空(没有成员变量)的,没有成员,那就是1个字节,不存数据,就标识有这个对象存在过。
Date类中有print和init成员函数,由上可知他们存储在相同位置,当我们创建不同Data的不同对象时,如何分辨是谁的函数呢?那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这⾥的问题。
编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this 指针。⽐如Date类的Init的真实原型为, void Init(Date* const this, int year, int month, int day)
类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this- >_year = year;
C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显 ⽰使⽤this指针。
- #include
- using namespace std;
- class Date
- {
- public:
- // void Init(Date* const this, int year, int month, int day)
- void Init(int year, int month, int day)
- {
- // 编译报错:error C2106: “=”: 左操作数必须为左值
- // this = nullptr;
- // this->_year = year;
- this->_year = year;
- this->_month = month;
- this->_day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
- int main()
- {
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
- // d1.Init(&d1, 2024, 3, 31);
- d1.Init(2024, 3, 31);
- d1.Print();
- d2.Init(2024, 7, 5);
- d2.Print();
-
- return 0;
- }