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

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

个人主页:CSDN_小八哥向前冲

所属专栏:C++入门

目录

类的定义

访问限定符

类域

类的实例化

实例化概念

对象大小

this指针

类的默认成员函数

构造函数

析构函数

模拟栈(初学者)


类的定义

  • class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省 略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的⽅法或 者成员函数。
  • C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是 struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
  • 定义在类⾯的成员函数默认为inline。

问题 :struct和class都可以定义类,那有什么区别呢?

  1. 既然开始学习C++了,不要老想着用C语言的那一套,在C语言中,Struct是用来定义结构体的,容易混,虽然C++兼容C,但是在一定程度上会混淆自己!
  2. 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个默认成员函数。

那编译器自己会生成,是不是就不用我们自己写了?其实不然!

我们写不写需要看俩个方面:

  • 第⼀:我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求。
  • 第⼆:编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现。

构造函数

构造函数的特点:

  1. 函数名与类名相同。
  2. ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)。
  3. 对象实例化时系统会⾃动调⽤对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显 式定义编译器将不再⽣成。
  6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函 数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成 函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫 默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调 ⽤的构造就叫默认构造。
  7. 我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始 化是不确定的,看编译器。

图:

在这里,构造函数的作用其实就是将这个类初始化,例如,在栈中的Init函数的作用(初始化),如果用C++实现就能直接构造!

析构函数

析构函数的特点:

  1. 析构函数名是在类名前加上字符~。
  2. ⽆参数⽆返回值。(这⾥跟构造类似,也不需要加void)。
  3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。
  4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
  5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会 调⽤他的析构函数。
  6. 还需要注意的是我们显⽰写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数。
  7. 如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;如 果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,如MyQueue;但是有资源申请时,⼀定要 ⾃⼰写析构,否则会造成资源泄漏,如Stack。
  8. ⼀个局部域的多个对象,C++规定后定义的先析构。

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

模拟栈(初学者)

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

cpp 复制代码
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;
};

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

相关推荐
励志成为嵌入式工程师22 分钟前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉1 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer1 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq1 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java2 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山2 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
青花瓷2 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
睡觉谁叫~~~2 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
音徽编程2 小时前
Rust异步运行时框架tokio保姆级教程
开发语言·网络·rust
观音山保我别报错2 小时前
C语言扫雷小游戏
c语言·开发语言·算法