C++天使的灵动心跳代码:类和对象(上)


经过部分的 C++ 知识过渡,我们将真正进入面向对象的部分,本章将对 C++ 中最重要的 类和对象部分进行初步介绍,这是步入 C++ 的门槛知识,为重中之重,学好类和对象,就已经半只脚踏入 C++ 了🤘

1.面向过程与面向对象

🚩面向过程
C语言所学的内容主要都是面向过程的,核心是过程(函数),将程序看作是一系列步骤的组合。它强调的是 “怎么做”,重点在于设计函数和函数之间的调用顺序来完成任务,整个过程是围绕操作步骤(函数)展开的

例如:在一个文件复制程序中,面向过程的思路是先打开源文件,然后读取源文件内容,接着打开目标文件,再将读取的内容写入目标文件,最后关闭文件

🚩面向对象
核心是对象,把现实世界中的事物抽象成对象,对象包含数据(属性)和操作数据的方法。它强调的是 “有什么”,重点在于定义对象的属性和行为,通过对象之间的相互作用来完成任务

例如:对于文件复制程序,面向对象的思路是创建文件对象,这些文件对象有自己的属性(如文件名、文件路径、文件内容等)和方法(如打开、读取、写入、关闭等),通过操作这些文件对象来实现文件复制

2.类

2.1 类的引入

类是一种数据类型,在C语言中类指的是结构体,但他只能包含变量;在C++中的类能包括变量和函数,清晰对一个对象进行属性行为说明,所以类和结构体十分相似

//以C语言环境
struct Stack
{
	int a = 0;
	char ch = 1;
	int* arr = &a;
};

//以C++环境
class Stack
{
	int a = 0;
	char ch = 1;
	int* arr = &a;
};

上面结构体的定义,在 C++ 中喜欢用class来代替,实际上在 C++ 中 class 和 struct 都可以用,只是 C++ 里更喜欢用 class 与 C 语言做区分

2.2 类的定义

其语法形式为

class className
{
	// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

🔥值得注意的是

class为定义类的关键字ClassName类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略
• 类体中内容称为类的成员:类中的变量称为类的属性成员变量; 类中的函数称为类的方法或者成员函数

2.3 类中的声明与定义

假如我们要描述一个学生:

🚩定义与声明全都放在类中

class student
{
	void show()
	{
		cout << _name << "-" << _sex << "-" << _age << endl; 
	}
	char _name;//姓名
	cahr _sex;//性别
	int _age;//年龄
};

一般我们平常没有做项目时就把成员函数的声明定义都放在一起

🚩定义与声明分开

//test.h
class student
{
	void show();
	char _name;//姓名
	cahr _sex;//性别
	int _age;//年龄
};
//test.cpp
#include "test.h"
void student::show()
{
	cout << _name << "-" << _sex << "-" << _age << endl; 
}

一般情况下,更期望采用定义与声明分开方式,因为以后在写项目通常会分多个文件。由于是在类域外访问成员函数,所以要加上作用域限定符

2.4 类的访问限定符及封装

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

访问限定符分为

• public(公有)
• protected(保护)
• private(私有)

⌨️举个例子

class Date
{
	public:
		 void Init(int year)
		 {
			 _year = year;
		 }
	private:
		 int _year;
};

通常我们把成员变量放在私有(成员函数放在私有的情况后面会讲),成员函数放在公有,私有的是无法直接访问的,放在私有的变量一般通过成员函数访问(前提是成员函数在类域内,不在类域内要使用友元函数,后面会讲)

🔥值得注意的是

  1. public 修饰的成员在类外可以直接被访问
  2. protected 和 private 修饰的成员在类外不能直接被访问
  3. protected 和 private 目前是一样的,都是私有,其区别到了继承才体现出来
  4. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  5. 如果后面没有访问限定符,作用域就到 } 即类结束

为什么在私有成员变量会在变量名前加个 _ ?

// 我们看看这个函数,是不是很僵硬?
class Date
{
	public:
		 void Init(int year)
		 {
			 // 这里的year到底是成员变量,还是函数形参?
			 year = year;
		 }
	private:
		 int year;
};

通常私有成员变量会在变量名前加个 _ ,这是为了和传参的时候的名字做区分,避免不好区分变量,导致私有变量错误访问

🔥值得注意的是:私有成员变量在这里是声明,就算写成int _year = 0也只叫作给缺省参数,真正区分变量是否为定义还是声明在于变量有没有开空间

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

class MyClass 
{
    int myVariable; // 默认是 private 访问权限
};

struct MyStruct 
{
    int myVariable; // 默认是 public ![请添加图片描述](https://i-blog.csdnimg.cn/direct/f4fdd1e8a3244e58a6dca3126594c320.jpeg)
访问权限
};

如果不写访问限定符,在 MyClass 中,myVariable 外部不能直接访问,而在 MyStruct 中,myVariable 外部可以直接访问

2.5 类的作用域

C++命运石之门代码抉择:C++入门(上)中我们详细介绍了域的概念

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

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

void Person::PrintPersonInfo()
{
	 cout << _name << " "<< _sex << " " << _age << endl;
}

这里需要指定 PrintPersonInfo 是属于Person 这个类域

2.6 类的实例化

用类类型创建对象的过程,称为类的实例化

int main()
{
	 Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
	 return 0;
}

还是上面的类,这种实例化方式是错误的

打个比方:类实例化出对象就像现实中使用建筑设计图建造出房子就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

正确的实例化

int main()
{
     Person student;
	 student._age = 10;
	 student._name = "Jack";
	 student._sex = male;
	 student.PrintPersonInfo();
	 return 0;

先创建对象,才能对类中的成员进行操作

2.7 类的存储与大小

类是如何存储的?

如果要把所有成员都包括在类的存储中,按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?

⌨️所以在存储方式上类只保存成员变量,成员函数存放在公共的代码段,这就有效的减少了空间浪费,每个对象中成员变量是不同的,但是调用同一份函数,函数只在使用的使用的时候去公共部分调用

通过对下面的不同对象分别获取大小来分析

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

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

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

• sizeof(A1) : 4
• sizeof(A2) : 1
• sizeof(A3) : 1

🔥值得注意的是:类的大小计算也遵循内存对齐规则;为什么类中仅有成员函数空类的内存为大小为 1 byte,内存计算不是只考虑变量吗?原因其实非常简单,如果对这两种情况的类取地址,没有空间的话就会程序报错,但是这两个类又是真实存在的,所以定义这两种类的大小为 1 byte ,是为了占位,表示对象存在,不存储有效数据

3. this指针

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, d2;
	 d1.Init(2022,1,11);
	 d2.Init(2022,1, 12);
	 d1.Print();
	 d2.Print();
	 return 0;
}

当 d1 调用 Init 函数时,该函数是如何知道应该设置 d1 对象,而不是设置 d2 对象呢?

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

在这里插入图片描述
如图所示揭露了编译器的隐藏操作,其实在调用函数时传递了对象的地址

this指针可以为空吗?
如果 this 指针没有访问对象的成员,那么可以置空;如果 this 指针需要访问对象的成员,就不能为空,不然会导致程序报错,非法访问内存地址

🔥值得注意的是

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

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

评论 236
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值