类和对象(上)

1、面向过程和面向对象

在C语言中,我们所学的都是面向过程的,更加关注的是代码执行的过程,分析解决问题的步骤,通过函数调用来逐步解决问题。
在C++中,我们基于的是面向对象,将一件事情拆分成不同的对象,靠对象之间的交互来完成。

这两个我们可以这样理解:
我们平常都会点外卖,那么外卖中的关系在面向过程和面向对象是不同的。
面向过程:下单、接单、送餐三个过程
面向对象:客户、商家、骑手三者的关系

也就是说,C++将C语言中的过程转化成不同的对象交互来完成。

2、类的引入

既然说C++是面向对象的,那么C++怎么表示对象呢?
在C语言中。想要定义一个学生的类型,我们用的是结构体,可以这样定义:


struct Stu
{
	char name[20];    // 姓名
	int age;          // 年龄
	char StuCard[10]; // 学号
	char tele[12];    // 电话
};

在C++中,我们同样可以用结构体来定义一个类型。不同的是,C语言的结构体只能定义变量;而C++中的结构体不仅可以定义变量,还可以定义函数,这也就是将其称为类的原因。

struct Stu
{
	//输入信息函数
	void StuInfo(const char* name, const int age, const char* StuCard, const char*, const char* tele)
	{
		strcpy(_name, name);
		_age = age;
		strcpy(_StuCard, StuCard);
		strcpy(_tele, tele);
	}
	//打印信息函数
	void Print()
	{
		cout << name << " " << age << " " << StuCard << endl;
	}

	char _name[20];    // 姓名
	int _age;          // 年龄
	char _StuCard[10]; // 学号
	char _tele[12];    // 电话
};

在C++的结构体中,我们可以定义一些与该类型相关的功能函数,将它们与成员变量放在一起,称为类,就是说这些变量和定义是一种类型,比如说上面定义的学生类型,除了应该有的成员变量之外,还有打印信息和输入信息的函数,这两个函数和学生类型是密切相关的,那么就可以放到一个类型中。

在C++中,对于这种类型的定义,更喜欢用class代替

3、类的定义

class className
{

	//类的主体:由成员函数和成员变量组成
	
}; //这个分号要注意,一定要带上

类的定义中:
class 是定义类的关键字,相当于结构体的struct
className 为类的名字
{ } 内为类的主体部分,由成员函数和成员变量组成,这两个统称为类的成员

类的两种定义方式

(1)声明和定义都放在类的主体中

class Stu
{
	//输入信息
	void StuInfo(const char* name, const int age, const char* StuCard, const char*, const char* tele)
	{
		strcpy(_name, name);
		_age = age;
		strcpy(_StuCard, StuCard);
		strcpy(_tele, tele);
	}
	//打印信息
	void Print()
	{
		cout << name << " " << age << " " << StuCard << endl;
	}
	//成员变量在这里也是声明,只有在实际使用的时候开空间才是定义
	char _name[20];    // 姓名
	int _age;          // 年龄
	char _StuCard[10]; // 学号
	char _tele[12];    // 电话
};

这里要说明几点:
a. 主体中的成员变量在类中也是声明,只有在实际使用时开辟空间了才是定义。
b. 类中的成员函数如果在类中定义,编译器有可能当作内联函数来处理。

(2)声明放在.h文件,类的定义放在.cpp文件
在这里插入图片描述
在一般情况下,更希望采用第二种方式,这样能够更好的进行封装和功能的独立。

4、类的访问限定符和封装

访问限定符

C++类的引入使得对象之间有更好的独立性,那么在类中怎样实现封装的呢?

C++实现封装的方式:用类将对象的成员变量和成员函数结合在一起,使对象更加完善,通过访问限定符选择性的将其接口提供给外部的用户使用。

也就是说,不同的访问限定符,使得类中的成员有着不同的权限。

在这里插入图片描述
图中介绍了三种访问限定符以及其作用,还有两点需要补充:
(1) 访问权限的作用域从该访问限定符出现的位置开始到下一个访问限定符出现终止。
(2) class 的默认访问权限为private,而struct 默认访问权限为public(struct 要兼容C语言)。
在这里插入图片描述
左边定义的两个类,一个是用class定义的A类,p1是用A类定义的变量,可以看到在main函数中是不能访问p1中的a变量,说明class默认访问权限是private;另一个是用struct定义的B类,p2是用B类定义的变量,可以访问p2中的c变量,说明struct默认访问权限是public。

封装

封装指的是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互。

举一个例子:大家都去过很多博物馆,一般博物馆都会展示很多的文物。如果说我们把文物就随意的放置,什么都不管,那必然会受到破坏。那么为了更好的管理这些文物,首先会建一座房子,将所有的文物都放在里面,避免天气等原因对其造成破坏;其次在房子里面,文物还会用玻璃罩子将其罩住,因为文物都是有很多年头的,不能随便触摸。最后我们开放售票通道,买了票的人才能进来观看。

这个例子中,大房子相当于我们建一个工程,将我们要用的东西都放在工程中;玻璃罩子就相当于类或者函数,在一个工程内部的独立功能实现单独管理;买票就相当于将工程内部那些公有的东西放出来,外部成员可以看到。因此封装的本质是一种管理。

5、类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在一个类中,并不存在谁先定义谁后定义的问题,如果在函数中出现了一个变量首先会遵循就近原则,在函数内找,如果没找到,就会在整个类中找,因此在类中,所有的内容是一个整体。

在类的主体之外,如果要访问类中的公有成员,需要用域限定符" :: "指明成员属于哪个类域;私有成员在外部是不能访问的。
在这里插入图片描述

6、类的实例化

对于类有几点说明:
(1) 类只是一个模型一样的东西,表示了类中有哪些成员,但是并没有分配实际的内存空间来存储。
(2) 通过类可以实例化出一个或多个对象,只有实例化之后的对象,才会占用实际的物理空间,来存储类成员变量。
(3) 类在定义的时候像是建造房子的图纸,而实例化就是将图纸上的东西实现出来。
在这里插入图片描述

7、类对象的大小及存储方式

存储方式

一个类中,既有成员变量,也有成员函数,那么是怎样存储的呢?
先看下面一个代码:

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};

int main()
{
	Person man;
	Person woman;
	man.PrintPersonInfo();
	women.PrintPersonInfo();
	return 0;
}

用Person类定义了两个变量man和woman,同时调用PrintPersonInfo函数。如果在对象中同时包含所有的成员,man和woman中成员变量是不同的,但是成员函数调用的却是同一份,这样就会浪费空间。

为了节省空间,在对象中只保存成员变量。因为这部分对于不同的变量是不一样的;而将成员函数放在公共的代码段。
在这里插入图片描述
对于上面的存储方式,可以在计算机中进行验证:

class A1
{
public:
	void f1()
	{}
private:
	int _a;
};

//类中只有成员函数
class A2
{
public:
	void f2()
	{}
};

//类中什么也没有
class A3
{

};

int main()
{
	cout << "A1:" << sizeof(A1) << endl;
	cout << "A2:" << sizeof(A2) << endl;
	cout << "A3:" << sizeof(A3) << endl;

	return 0;
}

对于上面三种情况,得到的结果如下:
在这里插入图片描述
可以看到:
A1的大小是4,也就是int的大小
而A2和A3的大小都是1,因为它们都没有成员变量,计算的时候相当于空类,空类比较特殊,编译器给空类一个字节来唯一标识。

从这里可以看到,类的存储方式如同我们上述,对象中只存储成员变量,成员函数存在公共代码段。

类对象大小的计算

既然类中只有成员变量,计算的规则和结构体的计算规则是相同的。

结构体的对齐规则:
1.第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
(对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值)。
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

8、this指针

首先定义一个日期类来对this进行讲解。

class Date
{
public:
	void Display()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	void SetDate(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}


private:
	//C++命名风格,最好加上_
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1, d2;
	d1.SetDate(2021, 5, 27);
	d2.SetDate(2021, 5, 26);
	d1.Display();
	d2.Display();
	return 0;
}

在这里插入图片描述
我们可以看到对于对象d1和d2能够成功设置日期并打印,但是在调用函数的时候并没有指定对象,那么函数是怎么知道设置的是哪个对象呢?

答案就这节的主题:this指针。
C++通过引入this指针来解决,C++的编译器给每个“非静态的成员函数”增加了一个隐藏的指针,也就是this指针,通过这个指针指向当前对象,函数体中所有成员变量的操作,都是通过this指针来访问的。

我们可以验证一下this指针的存在:
在这里插入图片描述可以看到,d1和d2的地址与在Display函数中this指针的地址相同,说明成员函数是通过this指针来访问对象的。

关于this指针:
(1) 不能显式地在调用和函数定义中加this指针。
(2) 可以在成员函数中使用this指针。
(3) this指针一般存储在栈上,VS编译器中使用ecx寄存器来存储this指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值