目录
1.类的定义
1.1 类定义格式
补充: struct与class的区别(c语言与c++)
1.2 访问限定符
1.3 类域
2.实例化
3.对象大小
4.this指针
1.类的定义
1.1 类定义格式
class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省 略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的⽅法或 者成员函数。
为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_或者m 开头,注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求。
C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是 struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
定义在类⾯的成员函数默认为inline。
他的定义很简单,我们来解释一下inline,inline我们这里先简单介绍一下:inline函数指的是内联函数,在代码运行过程中不会出现栈帧,而是直接在原位展开,给一段简单的代码:
像这个print函数,在main函数中实际调用的时候,就会直接展开,我们看一下他的汇编代码
此时他没有调用函数的call指令(有的话代表着创建了函数栈帧,而不是原地展开)。
下一个需要强调的地方,c++中的struct基本就是class,他的用法和在c语言中的还不一样,我这里简单写个例子:
补充: struct与class的区别(c语言与c++)
C++:
但我们相同的代码放到c语言中时
报错,那我们应该怎么改呢?
法一:
此时原理是将struct Date重命名成了Date,所以能直接使用。
法2:
C++这里对c语言中的struct进行了改变,所以不要以为他们是一样的东西,其中还有一个区别是c语言不允许结构体中出现函数,
而c++却支持:
好,讲的讲的就跑偏了,我们切回正题。
1.2 访问限定符
1.C++⼀种实现封装的⽅式,⽤类将对象的属性与⽅法结合在⼀块,让对象更加完善,通过访问权限 选择性的将其接⼝提供给外部的⽤⼾使⽤。
2.public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访 问,protected和private是⼀样的,以后继承章节才能体现出他们的区别。
3.访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有 访问限定符,作⽤域就到}即类结束。
4.class定义成员没有被访问限定符修饰时默认为private,struct默认为public。
5.⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。
这个很好理解吧,简单举个例子:
1.3 类域
1.类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤::作 ⽤域操作符指明成员属于哪个类域。
2.类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全 局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知 道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
2.实例化
什么是实例化,简单来说:
d1,d2就是两个实例化出来的两个具体对象
注意注释:声明!
3.对象大小
这里会遵循一个叫做内存对齐的原则,在计算过程中,这个我之前讲过,look this:结构体内存对齐(你可能不知道但很重要的知识)-CSDN博客
大家可以看一眼,这部分和c语言是一样的,but ,c++中的类和c语言中的结构体的一个显著区别,类中支持函数,那函数占多大呢?我们来实践一下
接着我们把函数注释掉:
答案显而易见,就是不会去额外占用空间,为什么呢?
函数不会存在对象内部
⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令 存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针。再分析⼀下,对 象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量 _year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象 中就浪费了。如果⽤Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。这⾥需 要再额外哆嗦⼀下,其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指 令[call 地址]
4.this指针
每一个实例化之后的对象,都有一个this指针指向他,可以访问内部的成员变量
Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和 Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到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指针。
这就是类和对象的一个基础知识,我打算分三篇来讲类和对象,这是第一篇,接下来有两个题目给大家做一下
下⾯程序编译运⾏结果是() A、编译报错 B、运⾏崩溃 C、正常运⾏
#include<iostream>
using namespace std;
class A
{
public:void Print(){cout << "A::Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
答案应该是正确运行,大家可以去复制上运行一下,为什么呢?
为什么呢?明明p是个空指针但还是能够运行?原因在于,没有对p进行解引用,而且,我们在之前说过,成员函数不在对象内部存储,且在成员函数内部,也没有调用成员变量,所以最后能够正确运行。
#include<iostream>
using namespace std;
class A
{
public:void Print(){cout << _a;cout << "A::Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
那这段代码的运行结果呢?答案是崩溃了,首先不能是编译错误,因为编译解决的是语法问题,这个题语法没有错误,然后为什么是崩溃,因为他在函数内部进行了对成员访问,而this指针是个空指针