Bootstrap

类和对象(上)

目录

1.面向过程和面向对象

2.类

什么是类

类的定义

3.封装

4.访问限定符

5.类的实例化

6.类对象模型

7.隐藏的this指针

认识this指针

this指针的特性


1.面向过程和面向对象

在学习类和对象之前,我们首先要理解一下面向过程和面向对象。

我们以洗衣服为例理解面向过程面向对象

在用面向过程思想解决问题的时候,我们关注的是求解问题的过程,分析出求解问题的步骤,通过函数调用一步一步解决问题。在上面洗衣服的问题中,就将洗衣服拆分成了多个步骤,完成所有的步骤就能完成整个问题。

在用面向对象思想解决问题的时候,我们关注的是构成问题的对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。在上面洗衣服的问题中,总共有四个对象,分别是人、衣服、洗衣液、洗衣机;整个过程依靠对象之间的交互完成,并不关系具体的步骤细节。

2.类

我们使用C语言编写程序的时候,就是利用了面向过程的编程思想,解决问题的步骤通过函数调用来实现。而C++是一门具有面向对象特点的编程语言,它是如何体现自己的面向对象的呢?类就是C++面向对象的体现。

什么是类

面向对象的语言关注的是一个个的对象,所以,面向对象的编程语言必须要提供描述对象的能力,用来描述对象的就叫做类

那么如何描述对象呢?对于这个问题,我们只需要考虑对象有什么?对象拥有的东西可以划分为两类,一类是对象的属性,另一类是对象的动作。用标准的语言来说就是成员属性、成员方法。

类的定义

类有属性,肯定有不同的属性,这些属性可能是不同的数据类型,在C语言中,能够表示不同数据类型的集合的只有结构体,在C++中,对结构体做了升级,不仅仅可以在结构体中定义变量,还可以定义函数,也就是说,在C++中结构体被升级成了类

stu类的定义:

struct stu 
{
	int age;
	string name;
	string tel;
	
	void eat()
	{
		//… 
	}
	
	void run()
	{
		//… 
	}
};

但是C++中更喜欢用class替换struct来定义类:

class stu 
{
	int age;
	string name;
	string tel;
	
	void eat()
	{
		//… 
	}
	
	void run()
	{
		//… 
	}
};

总结:在C++中定义类和定义结构体其实很类似,在结构体中定义函数,并且将struct改成class就是定义一个类。

上面定义类的方式只是C++中定义类的一种方式,也就是将成员函数的声明和定义全部放在类体中,需要注意的是,成员函数在类体中定义,默认是内联函数

还有一种定义类的方式就是将成员函数的声明和定义分离

  • 定义成员函数的时候,需要在成员函数的前面加  类名::,相当于指定类域,也就是告诉编译器这个函数在哪个类里面声明了。

3.封装

面向对象具有三大特性:封装、继承、多态,我们现在主要讨论封装。

什么是封装?

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

为什么要封装?

  • 封装本质上是一种管理,让用户更加方便的使用类。以汽车为例:汽车内部有发动机、各种齿轮、制动系统等等一系列的东西,可以说汽车内部结构是比较复杂的,但是使用汽车的时候,我们只是简单的踩油门、刹车,转动方向盘。这其实就是汽车制造厂商隐藏了汽车内部的实现细节,对外提供统一的接口给用户使用,减小了用户的使用成本。

C++如何实现封装?

  • 在C++中,通过类将数据以及操作数据的方法进行结合,通过访问权限来隐藏类内部的实现细节,提供公有的方法给外部使用。

4.访问限定符

在C++的类当中,类内成员的访问权限主要依靠访问限定符来控制,C++中的访问限定符主要有public、protected、private。

访问限定符的说明:

  • public表示公共的,被public修饰的成员在类里类外都能直接被访问。
  • protected表示保护的,被protected修饰的成员只能在本类子类中访问。
  • private表示私有的,被private修饰的成员只能在本类中访问。

访问限定符的一些注意事项:

  • 访问权限的作用域从该访问限定符出现的位置开始,直到下一个访问限定符出现为止。
  • 如果后面没有访问限定符,作用域就到类结束的位置。
  • class定义的类,默认访问权限时private(更好的体现了封装);struct定义的类,默认的访问权限是public(因为C++要兼容C语言)。

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

5.类的实例化

前面我们学习过类的定义,定义出来的类相当于一个自定义的类型,类型是用来干嘛的?类型不就是用来定义变量的吗?不过在C++中对于自定义类型的变量更喜欢用对象来称呼。用类定义对象就叫做类的实例化。

我们定义出来的类并没有实际的内存空间,他只是一张图纸一样的东西;类实例化出的对象才会占用实际的物理空间,存储成员变量。

class stu 
{
private:
	int age;
	string name;
	string tel;
public:
	void eat()
	{
		//… 
	}
};

int main()
{
    stu s1; // 类的实例化
    return 0;
}

总结:类和对象之间的关系就相当于类是对象的模板,对象是类的实例

6.类对象模型

前面我们讨论的都是类的相关知识,现在我们来学习对象的相关知识。

类中既有成员变量又有成员函数,那么一个类对象中包含了什么?如何计算一个类类型的大小呢?

我们可以通过下面这份代码求证:

#include <iostream>
using namespace std;

// 类中既有成员变量,又有成员函数
class A
{
public:
	void f(){}
private:
	int _a;
};

// 类中仅有成员函数
class B
{
public:
	void f() {}
};

// 类中仅有成员变量 
class C
{
private:
	int _c;
};

// 类中什么都没有---空类
class D
{};

int main()
{
	cout << "sizeof(A): " << sizeof(A) << endl;
	cout << "sizeof(B): " << sizeof(B) << endl;
	cout << "sizeof(C): " << sizeof(C) << endl;
	cout << "sizeof(D): " << sizeof(D) << endl;
	
	return 0;
}

运行结果如下:

通过观察可知,当类中出现一个成员变量的时候,类的大小就是这个成员变量的大小;当类中啥也没有的时候,类的大小是1字节;成员函数似乎并不影响类的大小。

于是,我们可以得出结论:一个类的大小就是类中的成员变量之和(要注意内存对齐),空类的大小比较特殊,编译器给空类的对象开一个字节的空间,这个字节并不用来存贮数据,而是用来标识定义的对象存在。


那么问题又来了,类对象中只有成员变量,那成员函数到底存放在哪呢?

我们可以对比生活中的例子来理解,我们每个人都有独属于自己的物品,只有自己能用,但是,每个人在社会上都能使用社会提供的公共的设施、服务…… 这部分公共的,大家都要用的东西就没必要各自都存有一份了,否则就会造成空间的浪费。

类对象模型的设计者也是这么想的。每个类对象的属性肯定会有所不同,所以,类对象中必须要保存属性,但是对象调用的成员方法都是相同的,如果每个对象中都存一份成员方法,势必会造成空间的浪费,所以,这部分公共的东西可以存放在公共的区域。也就是说,类对象中只保存成员变量,成员函数放在了公共的代码段。

7.隐藏的this指针

认识this指针

问题:当我们用一个类定义出多个对象之后,并用这些对象调用同一个成员方法,这个成员方法是如何区分不同的对象,并使用该对象的资源的呢?

C++通过引入this指针解决该问题: 

具体过程如下:

  • C++编译器给每个非静态的成员函数增加了一个隐藏的指针参数(this指针),在函数运行时,让该指针指向调用该函数的对象。
  • 在函数体中,所有访问成员的操作都是通过该指针去访问的。
  • 传递参数给this指针,通过this指针访问成员的操作都是由编译器自动完成的。

this指针的特性

1.this指针的类型是:类类型 * const。比如上图中this指针的类型为 stu * const。

  • const在 * 的右边,修饰的是指针本身不能改变,说明非静态成员函数内部的this指针不能改变,这也符合我们的认知;因为this指针指向当前调用函数的对象,如果this指针改变了,就找不到当前对象中的成员了,这是不合理的,所以this指针不能改变。

2.this指针本质上是一个形参:

  • 既然是形参的话,就只能在该函数内部使用。
  • 作为函数的形参,在调用该函数的时候,该函数的参数需要入栈(栈区),所以this指针是存储在栈区的,对象中不存储this指针
  • this指针是一个隐藏的形参,由编译器自动传递,不能显示的传递,也不能在参数列表显示的写this形参,但是可以显示的调用

3.this指针不能为空:

  • 当this指针为空的时候,在函数内部访问成员需要使用this指针,也就会访问空指针,会造成程序崩溃!

  • 如果this指针为空,但是不访问成员,也就不会访问空的this指针,程序可以正常运行,但这是没有意义的。

综上,this指针最好不要为空。

;