【C++】透析类和对象(上)

小八哥向前冲~ 2024-07-28 16:35:01 阅读 63

有不懂的,可翻阅我之前文章哦!

                 个人主页: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++规定后定义的先析构。

在这里,析构函数的作用就相当于销毁!

模拟栈(初学者)

我们可以利用上述知识,浅浅构造一个简单的栈!

<code>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;

};

今天的类和对象就学到这里,我们下期见!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。