有不懂的,可翻阅我之前文章哦!
个人主页:CSDN_小八哥向前冲
所属专栏:C++入门
目录
类的定义
访问限定符
类域
类的实例化
实例化概念
对象大小
this指针
类的默认成员函数
构造函数
析构函数
模拟栈(初学者)
类的定义
- class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省 略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的⽅法或 者成员函数。
- C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是 struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
- 定义在类⾯的成员函数默认为inline。
问题:struct和class都可以定义类,那有什么区别呢?
- 既然开始学习C++了,不要老想着用C语言的那一套,在C语言中,Struct是用来定义结构体的,容易混,虽然C++兼容C,但是在一定程度上会混淆自己!
- Struct在没有添加访问限定符时,默认为public,而class默认为private。
访问限定符
- public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,它们俩的区别,我们现在不做深究!
- ⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。
类域
类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤::作⽤域操作符指明成员属于哪个类域。
类的实例化
实例化概念
- ⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
- 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。
- ⼀个类可以实例化出多个对象,实例化出的对象 占⽤实际的物理空间,存储类成员变量。打个⽐⽅:类实例化出对象就像现实中使⽤建筑设计图建造出房⼦,类就像是设计图,设计图规划了有多 少个房间,房间⼤⼩功能等,但是并没有实体的建筑存在,也不能住⼈,⽤设计图修建出房⼦,房 ⼦才能住⼈。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。
上图理解:
总结理解:
定义一个类的过程中,只是起到了声明作用,并没有开辟空间占用内存,当实例化出一个对象来,这才开辟了一块内存!
对象大小
开辟出一个空间来,那这个空间大小是多少呢?
简单来说,它的空间占用情况和C语言里面的结构体一模一样!也就是说我们计算一个对象大小也就是算C语言里面的结构体大小!
如果忘了C语言中结构体计算可以去这篇文章:结构体和联合体的计算_小八哥向前冲
我们来一起复习一下:
- 第⼀个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
- 注意:对⻬数=编译器默认的⼀个对⻬数与该成员⼤⼩的较⼩值。
- VS中默认的对⻬数为8。
- 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩ 就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍。
那如果没有成员只有函数呢?
这里放1的原因:
为什么没有成员变量还要给1个字节呢?因为如果⼀个字节都不给,怎么表⽰对象存在过呢!所以这⾥给1字节,纯粹是为了占位标识对象存在。
this指针
- Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和 Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这⾥的问题。
- C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显 ⽰使⽤this指针。
this指针方便更好地指向当前对象!调用类里面的函数,函数前面会隐含this指针,但是不能自己添加,但可以在函数里面调用this指针!
图理解:
类的默认成员函数
默认成员函数就是⽤⼾没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。
⼀个类,我 们不写的情况下编译器会默认⽣成以下6个默认成员函数。
那编译器自己会生成,是不是就不用我们自己写了?其实不然!
我们写不写需要看俩个方面:
- 第⼀:我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求。
- 第⼆:编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现。
构造函数
构造函数的特点:
- 函数名与类名相同。
- ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)。
- 对象实例化时系统会⾃动调⽤对应的构造函数。
- 构造函数可以重载。
- 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显 式定义编译器将不再⽣成。
- ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函 数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成 函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫 默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调 ⽤的构造就叫默认构造。
- 我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始 化是不确定的,看编译器。
图:
在这里,构造函数的作用其实就是将这个类初始化,例如,在栈中的Init函数的作用(初始化),如果用C++实现就能直接构造!
析构函数
析构函数的特点:
- 析构函数名是在类名前加上字符~。
- ⽆参数⽆返回值。(这⾥跟构造类似,也不需要加void)。
- ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。
- 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
- 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会 调⽤他的析构函数。
- 还需要注意的是我们显⽰写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数。
- 如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;如 果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,如MyQueue;但是有资源申请时,⼀定要 ⾃⼰写析构,否则会造成资源泄漏,如Stack。
- ⼀个局部域的多个对象,C++规定后定义的先析构。
在这里,析构函数的作用就相当于销毁!
模拟栈(初学者)
我们可以利用上述知识,浅浅构造一个简单的栈!
class stack
{
public:
stack(int n=4)
{
assert(n > 0);
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == nullptr)
{
perror("malloc failed!");
return;
}
arr = tmp;
top = 0;
capacity = n;
}
~stack()
{
free(arr);
arr = nullptr;
top = capacity = 0;
}
private:
int* arr;
int top;
int capacity;
};
今天的类和对象就学到这里,我们下期见!