C++:类和对象(上篇---概念、定义、访问限定符、封装、类的大小、this指针)

类和对象(上篇—概念及定义)

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。

一、类和对象概念

1.类: 类是现实世界在计算机中的反映,它将数据(成员变量)和对这些数据的操作(成员函数)封装在一起 (并没有开空间)
例如:定义一个简单的学生类如下,没有开辟空间
在这里插入图片描述
2.对象: 类的实例化 (占有实际的空间)
例如:实例化出zhangsan这个学生,开辟空间了
在这里插入图片描述
3.特点
① 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它,类不占用储存空间,类实例化出的对象才占用储存空间
一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量
③做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,类不能储存数据,类实例化出的对象才能实际存储数据,占用物理空间

二、类定义的两种方式

1.声明定义都在类体里
在这里插入图片描述
2.声明放在.h文件中,类的定义放在.cpp文件中,在实现其定义时要加上作用域限定符指明该函数属于哪一个类在这里插入图片描述

三、类的访问限定符

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

  1. public(公有):public修饰的成员变量或是函数可以在类外直接进行访问
  2. protect(保护)和private(私有):修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. class定义的类默认是私有的(private),而struct默认是公有的(public)----这是因为C++是兼容C语言的

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

【面试题】
问题:C++中struct和class的区别是什么?
解答:C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中struct还可以用来定义类。和class是定义类是一样的,区别是struct的成员默认访问方式是公有的class的成员默认访问方式是私有的

四、封装

【面试题】 面向对象的三大特性:封装、继承、多态。
在类和对象阶段,我们只研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

C++是基于面向对象的程序,面向对象有三大特性即:封装、继承、多态
C++通过类,将一个对象的属性与行为结合在一起,使其更符合人们对于一件事物的认知,将属于该对象的所有东西打包在一起;通过访问限定符选择性的将其部分功能开放出来与其他对象进行交互,而对于对象内部的一些实现细节,外部用户不需要知道,知道了有些情况下也没用,反而增加了使用或者维护的难度,让整个事情复杂化。

封装本质上是一种管理:我们如何管理兵马俑呢?比如如果什么都不管,兵马俑就被随意破坏了。那么我们首先建了一座房子把兵马俑给封装起来。但是我们目的全封装起来,不让别人看。所以我们开放了售票通道,可以买票突破封装在合理的监管机制下进去参观。类也是一样,我们使用类把数据和方法都封装起来,不想给别人看到的我们使用protected/private把成员私有起来。开放一些共有的成员函数对成员进行合理的访问。所以封装本质是一种管理。

五、类的大小

类大小的计算方法分为以下三种:
在这里插入图片描述
为什么有成员函数和成员变量时只计算成员变量呢?具体看下图
在这里插入图片描述
针对第一种情况需要详细说明一下:
有成员函数和成员变量时,类的大小只计算成员变量的大小,这里有多个成员变量时计算的是成员变量大小之和,要遵循内存对齐。 因为CPU在访问内存时不是一个字节一个字节读取的,这样读取太麻烦,CPU都是一次读一段,为了保证读取到数据的有效性就要保证内存对齐,虽然可能会有内存空间的浪费,但是这是一种以空间换时间的方法。修改默认对其数可以用#pragma pack(4)

结构体大小的计算请戳博主这篇文章:https://blog.csdn.net/ETalien_/article/details/82086145

六、this指针

首先看一段特别简单的Date类代码:

class Date
{
public:
	void Init(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		std::cout << _year << "-" << _month << "-" << _day << std::endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Init(2019, 1, 1);
	d1.Print();

	Date d2;
	d2.Init(2019, 2, 23);
	d2.Print();

	system("pause");
	return 0;
}

我们可以看到d1和d2是不同的对象,但是我们会有疑问在Print成员函数上,d1和d2调用的是同一个Print函数,但是这里打印出来的却是各自的日期值,这是为什么呢?原来是这里有一个隐含的this指针,编译器在底层会用this指针来标识是哪一个对象,this指针是每个成员函数的第一个参数。

具体编译器怎么做的我们一起来看看:
在这里插入图片描述
C++编译器给每个“成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

那么this指针存在哪里呢?

我们知道了this指针本质上其实是一个成员函数的形参,那么函数的形参存在于栈空间上,所以this指针存在于栈空间

那么this指针可以为空吗?

我们进行测试一下
在这里插入图片描述
写了这么一段代码,那能不能编译通过?又能不能运行通过呢?
通过在vs下的测试我们发现可以编译通过,但是不能运行通过,因为运行Date类的Print成员函数时会对this指针进行解引用,空指针是不能进行解引用操作的。
综上所述,this指针可以为空,但是不能对空的this指针进行解引用操作。

this指针的特点
  1. this指针的类型:类的类型* const (因为this指针本身是不可以被修改的,但是this指针指向的内容是可以进行修改的,可以指向不同的对象)。
  2. 只能在“成员函数”的内部使用(this指针是隐含的参数不需要写出来,如果把this指针写在参数的位置上编译器则会报错,在成员函数内部可以使用,在成员函数内部如果你写了编译器就不写了,如果你没有写那么编译器会自行补上this指针)。
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ETalien_/article/details/87886020