C++类的认识(上)

问题:

1.面向对象和面向过程初步认识

2.类和认识与结构体做区别

3.封装特性

4.this指针的认识

5.this指针存在哪里?

6.this指针可以为空吗?

目录

1.面向对象和面向过程

2.类的引入

3.类的定义

类的两种定义方式:

4.类的访问限定符即封装

4.1访问限定符

问:C++中struct和class的区别是什么?

 4.2封装

4.3类的作用域

5.类的实例化

6.类的大小

7.this指针

7.1this指针的引入

 7.2this指针的特性

 面试题:

1.this指针存在哪里?

2.this指针可以为空吗?


1.面向对象和面向过程

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用的方式解决问题。

比如洗衣服这件事:我们可以将它分成以下几个步骤:

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

比如洗衣服这件事就可以分为四个对象:

人,衣服,洗衣粉,洗衣机

整个洗衣服的过程:人将衣服放入洗衣机,倒入洗衣粉,启动洗衣机,洗衣机就会完成洗衣过程并且甩干。

整个过程主要是:人,衣服,洗衣机,洗衣粉这四个对象之间交互完成的。人不需要关心洗衣服机具体是如何洗衣服的,是如何甩干的。

2.类的引入

我们知道了C++是通过对象之间的交互来完成的,那么对象如何描述呢?

现实描述一个对象由两个方面:属性 + 功能

还是用洗衣服来举例子:我们对洗衣机进行描述的时候就是对实体进行抽象的过程

人对洗衣机实体对象来进行描述:

属性:外观尺寸 ,重量 ,厂商 , 其他参数

功能:洗衣服 , 烘干 , 定时 , 响铃 等

描述的越详细,在他人的头脑中更具体

而计算机是不认识我们上面的描述的,那么我们就需要将其变成计算机认识的。

在C语言中我们学习过了结构体,但是C语言中的结构体中只可以有变量,不可以有函数,但是在C++这门语言中,我们的结构体中可以有变量,也可以有函数,也就是我们所说的成员变量和成员函数。

我们在C++中对一个对象的描述也就是:成员变量(属性) + 成员函数(功能)

例如:对一个洗衣机的描述:

struct WashMachine{
	//属性
	int size;//尺寸
	int Weight;//重量
	char ManuFac[32];//厂商

	//功能
	void Wash(){//洗衣服
		cout << "wash" << endl;
	}
	void Dry(){//烘干
		cout << "dry" << endl;
	}
	void Ring(){//响铃
		cout << "ring" << endl;
	}
};

上面结构体的定义,在C++中更喜欢使用class来代替,也就是我们所说的

那么已经有了结构体了为什么还要有类呢?别急,继续往下看

3.类的定义

class className
{
	// 类体:由成员函数和成员变量组成

}; // 一定要注意后面的分号

class为定义类时的关键字className为类的名字{}中为类的主体,注意类定义结束时后面的分号

类的主体由两部分构成:成员函数和成员变量。

类的两种定义方式:

1.定义和声明全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

2.声明放在.h文件中,类的定义放在.cpp文件中

一般建议使用第二种,声明和定义分开。 

4.类的访问限定符即封装

4.1访问限定符

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

问:访问限定符有几种?答:三种,public(共有),protected(保护),privat(私有)

  • public修饰的成员在类外就可以直接访问
  • protected和private修饰的成员在类外不能直接被访问(此处的protected和private是类似的)
  • 访问权限作用域从该访问限定符出现的位置直到下一个访问限定符出现时为止
  • class的默认访问权限为private,struct为public(因为struct要兼容C)(为什么要有class的原因之一)

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

问:C++structclass的区别是什么?

答:C++需要兼容C语言,所以C++struct可以当成结构体去使用。另外C++struct还可以用来定义类。 和class是定义类是一样的,区别是struct的成员默认访问方式是publicclass是的成员默认访问方式是private。

 4.2封装

面向对象的三大特征:封装、继承、多态

我们这里只研究封装。

先举个例子:函数就是对一条条语句的封装,我们在使用的时候就不用一句一句的进行写,直接调用函数就可以了。

printf这个函数就是我们将一条条的语句封装起来,才实现向屏幕打印的这个功能的,我们在使用它的时候不需要关心他的内部是怎么实现的,我们只需要使用它的功能就可以了。

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

4.3类的作用域

类定义了一个新的作用域 ,类的所有成员都在类的作用域中 在类体外定义成员,需要使用 :: 作用域解析符 指明成员属于哪个类域。
class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
	cout << _name << " "_gender << " " << _age << endl;
}

5.类的实例化

比如我们在现实生活中建房子的时候,我们需要先根据我们的需求画出施工的图纸(创建类的过程),然后我们需要根据图纸建造出房屋(类创建对象的过程,也就是类的实例化)。

用类类型创建对象的时候,称为类的实例化

1.跟建房的图纸一样,  类只是 一个 模型 一样的东西,限定了类有哪些成员,定义出一个类 并没有分配实际的内存空间 来存储它
2.一个图纸可以建造出多个房子,  一个类也可以实例化出多个对象, 实例化出的对象 占用实际的物理空间,存储类成员变量

6.类的大小

通过代码验证可以看到,一个类的大小只与它的成员变量有关和它的成员函数无关,而且空类的大小是1。 

 总结:一个类的大小,实际就是该类中成员变量之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。

注意:这里的成员变量之和不包含static修饰的静态成员变量。至于为什么,后面会提到。 

7.this指针

7.1this指针的引入

首先我们来看一个问题:

class Date{
public:
	void Init(int year, int month, int day){
		_year = year;
		_month = month;
		_day = day;
	}
	void Print(){
		cout << _year << '-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
    Date d2;
	d1.Init(2020,3,21);
    d2.Init(2020,3,22)
	d1.Print();
    d2.Print();
	return 0;
}

上述代码创建了d1和d2两个对象,函数中没有对于不同对象的区分,当我们调用Inti函数的时候,该函数是如何知道应该设置d1还是设置d2呢?

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

验证:this指针的存在(打印this指针)

通过上面的代码我们可以发现this指针的存在。

那么我们就可以根据this指针写出如下的代码:(Date类真正的代码)

class Date{
public:
	void Init(/*Date*this*/int year, int month, int day){
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print(/*Date*this*/){
		cout << this->_year << '-' << this->_month << '-' << this->_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2;
	d1.Init(/*&d1*/2020, 3, 21);
	d1.Print(/*&d1*/);
	d2.Init(/*&d2*/2020, 3, 22);
	d2.Print(/*&d2*/);
	return 0;
}

由上述代码可知我们在成员函数内部是通过this指针来访问成员变量的,而且这个过程对针对用户是透明的。

我们通过反汇编再开一下:

我们先将代码还原:

class Date{
public:
	void Init(int year, int month, int day){
		_year = year;
		_month = month;
		_day = day;
	}
	void Print(){
		cout << _year << '-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2;
	d1.Init(2020, 3, 21);
	d1.Print();
	d2.Init(2020, 3, 22);
	d2.Print();
	return 0;
}

反汇编查看:显而易见,this指针的传递以及访问成员变量的过程都是编译器帮我们做的,不需要我们用户自己完成。 

  

 7.2this指针的特性

  • 1. this 指针的类型:类类型 * const
  • 2. 只能在 成员函数 的内部使用
  • 3. this 指针本质上其实是一个成员函数的形参 ,是对象调用成员函数时,将对象地址作为实参传递给 this 形参。所以对象中不存储 this 指针
  • 4. this 指针是成员函数第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用户 传递

 面试题:

1.this指针存在哪里?

我们通过打印this指针的方式,查看main函数的esp和ebp寄存器

 通过查看我们发现this的地址就在esp和ebp之间,esp和ebp标记的就是Init函数的栈帧。

2.this指针可以为空吗?

可以。

我们虽然无法在成员函数的内部将this指针置为空,但我们可以在外部将this指针的指向置为空。如下:

但是,我们将this指针置为空之后就无法访问成员函数内部的成员了。如下:

猜你喜欢

转载自blog.csdn.net/qq_57822158/article/details/123636166