WHCSRL 技术网

【C++】类和对象的解释与分析(第1部分)

在这里插入图片描述


前言

关于C++中类的内容讲解,因为内容较多,一篇博客的书写可能会让人失去对完的信心,所以这一次我将通过多篇小幅度博客对类和对象进行多次讲解;如果有读者学习过java等语言,在阅读时会轻松许多


一、面向过程和面向对象初步认识

我们在学习C++时,我们知道C++最初的目的是弥补C语言的不足,同时其特性和当时新编写的语言相同,开始面向对象进行考虑,考虑各个对象之间的关系;

C语言是面向过程的,关注的过程,分析出解决问题的每个步骤,通过函数调用,逐步实现解决问题。

C++是面向对象的,关注的是对象,将一件事情拆分成不同的对象,依靠对象之间的交互完成


二、类的引入

在这里插入图片描述

我们在C原因中如果我们想要实现链表以及链表的增删查改等操作,我们定义的结构体中只能包含定义的变量,但我们的一些列操作函数却和我们的结构体分开后

而我们的C++中,我们的结构体中既可以包含我们定义的变量,同时还可以包含我们的一系列操作函数。

//代码举例:
struct ListNode//C语言
{
	int val;
	struct ListNode* next;
}

struct ListNode//C++
{
	int val;
	ListNode* next;//C++的特殊之一
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
//代码举例:
struct Student
{
 	void SetStudentInfo(const char* name, const char* gender, int age)//我们在结构体中定义函数
 	{
 		strcpy(_name, name);
 		strcpy(_gender, gender);
 		_age = age;
 	}
 
 	void PrintStudentInfo()//我们在结构体中定义函数
 	{
 		cout<<_name<<" "<<_gender<<" "<<_age<<endl;
 	}
 
 	char _name[20];//C和C++中定义的变量
 	char _gender[3];
	int _age;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

注意:我们在C++中更喜欢用clss来定义

//代码举例:
class Student
{
 	void SetStudentInfo(const char* name, const char* gender, int age)//我们在结构体中定义函数
 	{
 		strcpy(_name, name);
 		strcpy(_gender, gender);
 		_age = age;
 	}
 
 	void PrintStudentInfo()//我们在结构体中定义函数
 	{
 		cout<<_name<<" "<<_gender<<" "<<_age<<endl;
 	}
 
 	char _name[20];//C和C++中定义的变量
 	char _gender[3];
	int _age;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

三、类的定义

类的定义如下:

//代码举例:
class classname
{

	//类成员:函数的声明和变量的声明
	
};//一定要加上';'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注意:

class为定义类的关键字ClassName为类的名字{}中为类的主体,注意类定义结束时后面分号
类中的元素称为类的成员:
类中的数据称为类的属性或者成员变量;
类中的函数称为类的方法或者成员函数。

我们有两种类的定义方式:

在这里插入图片描述

在这里插入图片描述
这里我们更推荐第二种方式,因为当我们在类中定义函数时,如果我们的函数定义过长,函数定义无效,不会将其视为内联函数


四、类的访问限定符以及类的封装

在这里插入图片描述

4.1.访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

我们上面讲述的将变量和方法都定义在类中,其实就是类的封装;

现在我们先讲述一下类中的访问限定符
在这里插入图片描述
说明:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. class的默认访问权限为private,struct为public(因为struct要兼容C)


4.2封装

面向对象的三大特性:封装、继承、多态。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行,这里我们在上面的讲述中其实就是封装性的体现

我们对封装性进行一个现实生活中的一个例子:
故宫早期的开发让游客们可以随意观光,但此时带来的问题就是部分游客随意修改破坏,导致故宫部分收到损坏,而封装的出现就像是对故宫进行多方面的保护,比如设置管理员,划分观光区域等,使我们的故宫得以保护,不被破坏;对应到类中,我们一些变量不希望被人随意修改,这个时候我们通过封装,使得要想使用变量或者修改变量只能通过函数进行,使得我们的变量不会被任意修改使用,得以保护


五、类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。

我们举例说明:我们程序中有两个类,而这两个类中有两个同名的函数,当我们在对函数进行定义时,我们就需要明确函数的类域

//代码举例:
class PersonC
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;	
}

class PersonCPP
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
// 这里需要指定PrintPersonInfo是属于PersonCPP这个类域
void PersonCPP::PrintPersonInfo()
{
 cout<<_name<<" "_gender<<" "<<_age<<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

六、类的实例化

在这里插入图片描述

说明:

1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

在这里插入图片描述
在这里的类的实例化today、yesterday、tomorrow中都有变量_data、_month、_year但每一个实例化个体中的变量存储的信息都不同,就好像我们用同一张图纸建立了许多了栋楼房,楼房的结构都是一样大,但每一个楼房中住几个人,用的什么家具等都是不同的


七、类的存储形式

我们在创建一个类中,我们知道类中有变量和函数,那么我们一个类的实例化的大小就是我们的变量和函数在计算机中所占的大小吗?

在这里插入图片描述

在这里插入图片描述

答案并非如此,在类中,我们类的实例化的大小仅记录为类中变量所占的空间大小,那为什么我们不将类中的函数所占的空间记录到我们的实例化中呢?

这个时候我们去想,我们通过类的定义可以实例化许多对象,那么如果我们将类的定义中的函数也都放在每一个对象中时,使得我们的对象所占空间更大,如果我们的类中定义了许多函数,那么我们所需的栈帧空间太大,使得我们的栈帧开销可能不足后续使用。

所以我们类的实例化的大小仅即为变量所占的空间大小,我们将类中方法置于公共代码区,当我们的实例化需要使用时调用即可,减省栈帧空间的消耗。


八、类成员函数的this指针

我们在讲述this指针前先定义一个日期类Data

//代码举例:
class Date
{ 
public :
	void Display ()
	{
		cout <<_year<< "-" <<_month << "-"<< _day <<endl;
	}
    //void SedDate(Date* this,int year,int month,int day)
    //{
    //	this->_year = year;
    //	this->_month = month;
    //	this->_day = day;
    //}
	void SetDate(int year , int month , int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private :
	int _year ; // 年
	int _month ; // 月
	int _day ; // 日
};
int main()
{
	Date d1, d2;
	d1.SetDate(2018,5,1);//d1.SetDate(&d1,2018,5,1);
	d2.SetDate(2018,7,1);//d2.SetDate(&d2,2018,7,1);
	d1.Display();
	d2.Display();
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

我们在main函数中实现对对象d1和d2的变量初始化和变量的打印,但这个时候我们注意到我们在对对象d1和d2进行初始化时,没有像C语言中使用指针数据类型去作为参数接受实参,而只是传递了要改变的变量的数据,那这样是如何实现对对象的变量进行改变的呢?

另一个问题:Date类中有SetDate与Display两个成员函数,函数体中没有关于不同对象的区分,那当s1调用SetDate函数时,该函数是如何知道应该设置s1对象,而不是设置s2对象呢?

这里的解决办法实际上是:
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

我们在读上述的内容时仍可能不理解是什么意思,下面我们通过对代码进行图解进行讲述:
在这里插入图片描述


总结

这里我们对主要类的封装性和this进行了讲述,而这两个特点是我们C语言中不曾有过的特性,这里需要我们对其进行仔细理解;封装性是将我们的变量和函数进行统一定义,同时对不同的变量和函数进行访问限定符修饰;而this指针是我们函数中默认的实现,避免了我们再创建改变数据类型的指针,更方便我们函数功能的实现

以上是我对类和对象的个人认识,后续我们会继续讲解类和对象的内容

上述内容如果有错误的地方,还麻烦各位大佬指教【膜拜各位了】【膜拜各位了】
在这里插入图片描述

推荐阅读