【c++】类和对象(一)

小编个人主页详情<—请点击
小编个人gitee代码仓库<—请点击
c++系列专栏<—请点击
倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!在这里插入图片描述



一、面向过程和面向对象的简单认识

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

二、结构体的升级

在c语言中结构体struct只能定义变量,在c++中对其进行了全面升级,在c++语法中结构体不仅可以定义变量还可以定义函数

  1. 在c++语法中,结构体类型可以是struct Stack也可以是Stack
  2. 定义结构体变量的时候可以是使用 struct Stack st1 也可以是 Stack st2
struct Stack
{
	void Init()
	{
		a = (int*)malloc(sizeof(int) * 4);
		if (a == nullptr)
		{
			perror("malloc error");
			return;
		}

		top = 0;
		capacity = 4;
	}

	int* a;
	int top;
	int capacity;
};
  • 在c++中也有类似于结构体struct的事物名叫类,类则是使用class定义

三、类的定义

在c++中类是类似于结构体的,只不过在本该放struct的地方在c++中替换成为class加空格加类名加花括号,同时注意类似于结构体,在}的后面一定要跟;分号,这样就完成了一个类的声明

  1. 在类中花括号内部的成为类体,类体中的内容成为类的成员
  2. 在类中声明的变量称为成员变量或类的属性
  3. 在类中定义的函数称为成员函数或类的方法
  4. 关于这里的public和private叫做类访问限定符,小编会在后文进行讲述
  5. 类的类型就为类名className
class className
{
public:
	//成员函数
	int Add(int a, int b)
	{
		return a + b;
	}
private:
	//成员变量
	int x;
	int y;
};

1. 声明和定义不分离

  1. 声明和定义全部放在类体中,在类体中定义成员函数,这种定义方式编译器可能会将成员函数当作内联函数来处理
//Stack.h
class Stack
{
public:
	void Init()
	{
		a = (int*)malloc(sizeof(int) * 4);
		if (a == nullptr)
		{
			perror("malloc error");
			return;
		}

		top = 0;
		capacity = 4;
	}

private:
	int* a;
	int top;
	int capacity;
};

2. 声明和定义分离

  1. 声明放在.h文件,定义放在.cpp文件中。
  2. 类是一个新的作用域,名为类域,::是域作用访问限定符,在类体外定义成员函数,需要使用类域名字加::加成员函数名字来指明函数属于哪一个类域,进而才能在类域外部定义成员函数
//stack.h
class Stack
{
public:
	void Init();

private:
	int* a;
	int top;
	int capacity;
};

//stack.cpp
#include "test.h"

void Stack::Init()//需要使用域作用限定符::声明这个函数是在类域中
{
	a = (int*)malloc(sizeof(int) * 4);
	if (a == nullptr)
	{
		perror("malloc error");
		return;
	}

	top = 0;
	capacity = 4;
}

3. 成员变量命名风格

  1. 如果单纯命名成员变量不加特殊符号,有的时候我们的成员变量就会跟成员函数的形参冲突,造成难以区分,例如,
class Date
{
public:
	void Init(int year = 1, int month = 1, int day = 1)
	{
		year = year;
		month = month;
		day = day;
	}

private:
	int day;
	int month;
	int year;
};
  1. 通常来讲,命名成员变量的时候,我们使用在成员变量之前加入_下划线,这样可以很好的区分成员变量和函数形参
class Date
{
public:
	void Init(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _day;
	int _month;
	int _year;
};

四、类的访问限定符和封装

1. 类的访问限定符

  1. c++实现封装的方法,使用类将对象的属性(成员变量)和方法(成员函数)结合在一起(封在类域中),让对象更加完善,通过访问权限的限制,有选择的将接口提供给外部用户使用
  2. 访问限定符的作用域是从该访问限定符出现的位置开始到下一个访问限定符结束,如果没有下一个访问限定符,那么就到}花括号结束
    在这里插入图片描述
  3. 在c++中class和struct有什么区别?
  4. 在当前阶段,由于c++需要兼容c语言,所以strcut也可定义类,只不过strcut中成员的默认访问权限是共有的,在结构体/类外面可以直接访问,class用来定义类,class中成员的默认访问权限是私有的,在类外面不可以直接访问

2. 封装

  1. 面向对象的三大特性:封装、继承、多态
  2. 在当前的类和对象阶段,我们仅仅研究封装的特性
  3. 封装:将数据和操作数据的方法结合起来,隐藏对象的属性和实现细节, 仅对外公开接口用于对象之间的交互
  4. 封装的本质是一种管理,可以让用户更好的使用类。例如博物馆其实就有一种封装的思想,将文物放在博物馆中,使用玻璃围起来,安装监控,有专门的安保人员负责文物安全,前来参观的游客只能看,欣赏文物,不能使用手去触摸文物,这样文物就得到了很好的保护,试想一下,如果没有这些保护措施,那么是不是游客可能会有一些不文明的行为,比如,拿起小刀子刻画文物,在文物上刻下xxx到此一游,甚至可能文物会被偷走,那么这样文物的安全性就无法得到保障
  5. 在c++中的类,将对象的属性(成员变量)和方法(成员函数)封装起来,隐藏方法(成员函数)的实现细节,仅仅将函数的接口公开对外使用,那么用户就只能使用接口,不可以对对象的属性和方法的实现细节进行修改,这样就很好的保护了对象
    在这里插入图片描述

五、类的实例化

  1. 类似于结构体中使用结构体类型定义变量,在c++中使用类的类型定义的称为对象
  2. 使用类的类型创建对象的过程,就叫做类的实例化
  3. 类是对对象进行描述的,类就好比一个航海图图纸,它描绘了海上和陆地应该有的不同的区域,但是实际上它并不是真正的大海和陆地,类限定了对象中应该有什么成员,类定义了对象,但是没有实际分配内存空间去存储成员
    在这里插入图片描述
  4. 一个类可以实例化处多个对象,实例化出的对象分配实际的物理空间去存储成员变量
  5. 拿成员变量命名风格中代码中的日期类来说,我们将private屏蔽掉,即默认访问成员变量的权限变为public,但是你仍然不可以使用域访问限定符::去访问类中的成员变量,因为类并没有实际分配内存空间去存储成员变量,这就好比你拿起了一张航海图图纸,你就真的将实际的海洋和陆地举起来了吗?这固然是不可能的
//注意这是小编演示的错误示范,读者友友们请不要这样使用
int main()
{
	Date::_day;
	
	return 0;
}

在这里插入图片描述

六、类对象模型

1. 类对象的基本模型

  1. 类实例化出的对象中只存储成员变量
  2. 对象中不存储成员函数,因为如果每一个对象都存储成员函数,那么每一个对象中存储的成员函数又是相同的,对于空间会造成不必要的浪费,如果每一个对象都存储成员函数的地址,那么每一个对象中存储的成员函数的地址又是相同的,同样也会对空间会造成不必要的浪费
  3. 所以在c++语法中,类实例化出的对象干脆就不存储成员函数,而是转而将类中的成员函数放在一个公共区域——公共代码段,只要是由这个类实例化出的对象就都可以进行调用
  4. 对于有一个至多个成员函数的类对象的计算规则同结构体的内存对齐计算规则,关于结构体大小计算之内存对齐小编已经写好了详情请点击<—
  5. 对于一个都没有成员函数的类对象,编译器默认为它分配1字节的空间,这是为了占位,并不存储有效数据,确保由它实例化出的对象存在
  • 那么我们来进行一下测试,请读者朋友们计算一下类A,类B,类C的字节数大小
class A
{
public:
	void f1()
	{}

private:
	char a;
	int b;
};

class B
{
public:
	void f2()
	{}

private:
};

class C
{
public:

private:
};

int main()
{
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;
	cout << sizeof(C) << endl;


	return 0;
}

在这里插入图片描述

  1. 类A的大小为8字节,计算过程如下
    在这里插入图片描述
  2. 类B和类C都没有成员变量,所以为了占位,同时表示对象存在,类B,类C的大小都为1字节

1.为什么要进行内存对齐

  • 硬件原因:内存对齐可以提高内存访问效率,CUP在读取数据时,一次性读取固定字节大小。如果将多个变量,连续存放在内存中而不做处理,会出现读取数据不完全的情况,需要来回读取两次
  • 平台原因:并不是所有的硬件平台都支持在任意位置访问任意数据,某些硬件平台只支持在特定地址处处访问特定类型的数据,否则抛出硬件异常

2.如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐?

  • 可以使用#pragma pack(n),n为你想修改的对齐参数

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(2025, 3, 28);
	d2.Init(2025, 3, 29);
	d1.Print();
	d2.Print();
	return 0;
}

在这里插入图片描述

  1. 我们可以观察出类中含有两个成员变量Init()和Print(),并且小编使用类实例化处了两个对象d1,d2使这两个对象分别调用两个成员变量Init()和Print(),在调用函数中我们观察我们并没有显示传入d1或d2的地址,编译器是如何知道要对d1和d2进行初始化和打印的呢?
  2. c++中是通过this指针来进行解决这个问题,c++编译器给"非静态成员函数"增加了一个透明的指针参数,让指针指向当前对象,当前对象是指函数运行时调用函数的对象,所有成员变量调用成员函数的操作,都要通过这个this指针去访问成员函数的,这个this指针不需要用户主动传,而是由编译器默认传的
  3. 所以对于 d1.Print() —> 被编译器处理为 d1.Print(&d1)
    d2.Print() —> 被编译器处理为 d2.Print(&d2)
  4. this指针的类型是const类型的,即为Date * const this 可以看出在调用函数的时候this指针作为函数形参,this指针的本质为形参,所以this指针存在栈上,当调用函数建立栈帧,对象地址传给this指针,函数调用完成,栈帧销毁,this指针随之销毁,所以对象中并不存储this指针
    在这里插入图片描述
  5. this指针只能在成员函数内部进行调用
    在这里插入图片描述
  6. this指针不能显示写在成员函数参数中,只能由编译器进行默认调用
    在这里插入图片描述

总结

以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!

评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值