C++——类和对象(1)

一、前言

每日必问:“今天的你还在认真学习吗?”生活老是忙忙碌碌,可是你却不知道每天到底在忙些什么。所以我们要做的第一是学习,第二是总结,只有学习+总结才能学到真正的知识。博主最近也是在学习C++中学到了新的内容,先给大家交个底:我在学习这节新内容的时候可谓是受尽了折磨。那今天就看看你是否被折磨吧。言归正传,今天要分享的内容是C++——类和对象上。

二、类和对象上

2.1 类的定义

2.1.1 定义格式一

在C语言的数据结构与算法中,我们学习了栈——stack这个数据结构。那么在C++中用类由该如何实现呢?不着急,让我们先来看一段代码,如下:

#include <iostream>
#include <assert.h>
using namespace std;
class Stack//用类实现栈
{
public:
	void StackInit(int n = 4)//假设初始化数组个数为4
	{
	    arr = (int*)malloc(sizeof(int) * 4);
		if (arr == nullptr)
		{
			perror("malloc fail!");
			return;
		}
		capacity = n;
		top = 0;
	}
	void StackPush(int x)//插入+扩容
	{
		if (top == capacity)
		{
			int newcapacity = 2 * capacity;
			int* tmp = (int*)realloc(arr, sizeof(int) * newcapacity);
			if (tmp == nullptr)
			{
				perror("realloc fail!");
				return;
			}
			capacity = newcapacity;
			arr = tmp;
		}
		arr[top++] = x;
	}
	int StackTop()//取栈顶
	{
		assert(top > 0);
		return arr[top-1];
	}
	void StackDestroy()//销毁
	{
		if (arr)
			free(arr);
		arr = nullptr;
		capacity = 0;
		top = 0;
	}
private:
	int* arr;
	int top;
	int capacity;
};
int main()
{
	Stack st;
	st.StackInit();
	st.StackPush(1);
	st.StackPush(2);
	st.StackPush(3);
	st.StackPush(4);
	cout<<st.StackTop()<<endl;
	st.StackDestroy();
}

在这里插入图片描述

怎么样?看完上面这块代码,你肯定会感到非常的熟悉吧?这不就是栈的初始化以及一些功能的实现吗?但是你也会有疑问——怎么又和之前的不太一样呢?好多没有涉及到的知识啊?

1、class为定义类的关键字,class后面的Stack为定义的类的名字,{}中的是所定义的类的主体(需注意{}后有分号—;,这一点和struct结构体类似)。类中的内容称为类的成员:类中的变量称为类的属性或者成员变量,类中的函数称为类的方法或者成员函数。

2.1.2 定义格式二

接下来我们再看一段代码——它的具体作用是实现日期功能,如下:

include <iostream>
using namespace std;
class Date//日期功能
{
public:
	void DateInitPrint(int year,int month,int day)
	{
		cout << (_year  = year) << endl;
		cout << (_month = month) << endl;
		cout << (_day   = day) << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d;
	d.DateInitPrint(2025, 5, 12);
	return 0;
}

在这里插入图片描述
2、为了区分成员变量,一般会在成员变量之前加一个特殊标识符,如 _开头,m开头等等。但是并不强制,按照个人喜好或者特殊规定即可。

2.1.3 注意点

1、C++中struct也可以定义类,这是因为C++中也兼容C的语法,明显的变化是struct中可以定义函数,但是一般情况下我们都是用class定义类。
2、定义在class类里面的函数默认为inline

2.2 访问限定符

在上面两段代码中,都出现了public 和 private。那么它们到底有什么意义呢?其实public、private、protected它们都有一个统称,叫做访问限定符。

在这里插入图片描述

2.2.1 访问限定符的用法

1、public的意思为公共的,即public修饰的成员在类外可以被访问,private和protected的意思为私有的和受保护的,即private和protected修饰的成员在类外不可以被访问(注意:private和protected的作用是一样的)。

#include <iostream>
using namespace std;
class Date
{
public:
	int _year;
	int _month;
	int _day;
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _time;
	int _second;
};
int main()
{
	Date d;

	d.Init(2005, 5, 29);//类内成员函数定义
	cout << "类内成员函数定义:" << endl;
	cout << d._year << endl;
	cout << d._month << endl;
	cout << d._day << endl;

	cout << endl;

	cout << "类外访问public成员:" << endl;
	d._year = 2025;//类外访问public成员
	d._month = 5;
	d._day = 13;
	cout << d._year << endl;
	cout << d._month << endl;
	cout << d._day << endl;
	//public类外访问不会编译报错

  //cout << "类外访问private成员:" << endl;
   //d._time = 11;
   //d._second = 59;
   //cout << d._time << endl;
   //cout << d._second << endl;
   //private类外访问会编译报错
	return 0;
}

在这里插入图片描述
在这里插入图片描述

2、访问限定符的作用域是从该限定符的出现位置开始直到下一个访问限定符出现时为止,若之后没有访问限定符则一直到 },即类的作用域结束

3、class中定义的成员若没有被访问限定符修饰则默认为private/protected,struct默认为public.

在这里插入图片描述

4、为了避免风险,成员变量一般都会被限制为private/protected,如果别人需要使用的时候才会放为public。

2.3 类域

类会定义一个新的作用域,并且类的所有成员都在这个作用域中,如果在类外定义成员时,需要使用域操作符——::进行指明

#include <iostream>
#include <assert.h>
using namespace std;
class Stack
{
public:
	void Init(int n = 4);
	void Push(int x);
	int Top();
	void Destroy();
private:
	int* arr;
	int capacity;
	int top;
};
void Stack::Init(int n )
{
	arr = (int*)malloc(sizeof(int) * n);
	if (arr == nullptr)
	{
		perror("malloc fail!");
		return;
	}
	capacity = n;
	top = 0;
}
void Stack::Push(int x)
{
	if (top == capacity)
	{
		int newcapacity = 2 * capacity;
		int* tmp = (int*)realloc(arr, sizeof(int) * newcapacity);
		{
			if (tmp == nullptr)
			{
				perror("realloc fail!");
				return;
			}
		}
		capacity = newcapacity;
		arr = tmp;
	}
	arr[top++] = x;
}
int Stack::Top()
{
	assert(top > 0);
	return arr[top - 1];
}
void Stack::Destroy()
{
	if (arr)
		free(arr);
	arr = nullptr;
	capacity = 0;
	top = 0;
}
int main()
{
	Stack st;
	st.Init();
	st.Push(1);
	st.Push(2);
	st.Push(3);
	st.Push(4);
	cout << st.Top() << endl;
	st.Destroy();
	return 0;
}

如下图:我们在Stack类里面只对成员函数进行了声明,但是在类外我们又对成员函数进行定义,如果不使用域访问操作符::,在编译的时候就找不到arr,capacity,top这些私有成员。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.4 类的实例化

2.4.1 实例化的概念

在生活中,如果我们要修建一所房子,我们肯定会设计一张图纸,再根据这张图纸去实打实的盖房子。那么类的实例化也是一样的道理,**类是对对象的一种抽象化描述,类中的成员变量只是进行声明,没有分配空间,只有当用类去实例化对象的时候,才会分配空间。类实例化出来的对象并不是唯一的,可以实例化多个对象。**就像盖房子一样,一张图纸可以修建出多座房子。

在这里插入图片描述

2.4.2 实例化具体举例

前面我们写了一个Date日期类的代码,现在我们再进行温故而知新。

#include <iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << endl;
		cout << _month << endl;
		cout << _day << endl;
	}
private:  //这里只是声明,并没有实例化对象,所以并没有开辟空间
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d; //实例化,创建了对象,开辟空间
	d.Init(2005, 05, 29);
	d.Print();
	return 0;
}

在这里插入图片描述

2.4.3类的实例化对象大小

我们定义一个类,那么类中又有哪些成员呢?比如:成员变量、成员函数等等。那么当这个类实例化一个对象时,这个对象的大小又是多少呢?
在这之前,我们先来讲一讲内存对齐规则:
内存对齐是为了提高计算机的读写效率。对齐的地址通常是2、4、8的倍数。
不同类型的变量有不同的对齐要求,例如:

1字节的变量(如char)可以放在任意地址上。
2字节的变量(如short)放在2的倍数地址上。
4字节的变量(如int、float)放在4的倍数地址上。
8字节的变量(如double、long long)放在8的倍数地址上。

影响内存对齐的因素
变量排列顺序:变量在结构体中的排列顺序会影响对齐方式。
[&attribute((packed))&]:取消变量对齐,按照实际占用字节数对齐(仅gcc支持)。
#pragma pack(n):强制变量按照n的倍数对齐,并影响结构体结尾地址的补齐

内存对齐原则

1、数据成员对齐规则:结构体的第一个数据成员放在offset为0的地方,以后每个数据成员的起始位置要从该成员大小的整数倍开始。
2、结构体作为成员:如果一个结构体包含其他结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
3、收尾工作:结构体的总大小必须是其内部最大成员的整数倍,不足的要补齐。

怎么样?在了解完内存对齐规则之后,我们再来进行代码演练。

#include <iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << a << b << endl;
	}
private:
	int a;
	char b;
};
class B
{
public:
	void Print()
	{

	}
};
class C
{

};
int main()
{
	A a;
	B b;
	C c;
	cout << sizeof(a) << endl;
	cout << sizeof(b) << endl;
	cout << sizeof(c) << endl;
	return 0;
}

接着我们来看运行结果:
在这里插入图片描述
在A实例化的a中,int 一共占4个字节,这时满足内存对齐,char占一个字节,再进行内存对齐一共就是8个字节(具体画图展示如下)。

在这里插入图片描述

那么,成员函数Print的大小是否就为0了呢?可是事与愿违,当我们再来看B实例化的b的时候,b中只有一个Print函数,但是它的大小却占一个字节。如果再看C实例化的c,这只是一个空类啊?里面没有任何成员,那为什么这个空类也占一个字节呢?
对象a中大小有8个字节只是完全正确的,但是对象b和c中却各占1个字节这是因为这1个字节纯粹是用来表示占位作用的,并没有任何实际的含义,所以类中的成员函数是不占取空间大小的。

2.5 this指针

2.5.1 this指针的概念

在之前写日期类的时候,我们进行初始化时是:void Init(int year, int month, int day)。但是在C++中隐式包含一个this指针,这个指针的类型是当前的类类型,并且是默认出现在形参的第一个位置。那么Init的真正的原型则是:void Init(Date* const this,int year, int month, int day)

#include <iostream>
using namespace std;
class Date
{
public:
	//void Init(Date* const this, int year, int month, int day)
	void Init(int year, int month, int day)
	{
		//_year = year;
		//_month = month;
		//_day = day;

		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print()
	{
		//cout << _year << endl;
		//cout << _month << endl;
		//cout << _day << endl;

		cout << this->_year << endl;
		cout << this->_month << endl;
		cout << this->_day << endl;
	}
private:  //这里只是声明,并没有实例化对象,所以并没有开辟空间
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d; //实例化,创建了对象,开辟空间
	d.Init(2005, 05, 29);
	d.Print();
	return 0;
}

在这里插入图片描述
可以看出运行结果也是完全正确的。

但是C++规定this指针不能显示的出现在形参和实参的位置,但是在函数体内可以显示使用。并且this指针还可以根据不同的对象进行区分

2.5.6 this指针的实现过程

在这里插入图片描述

三、总结

怎么样,在听完博主讲解完类和对象第一部分的内容之后,你有没有一种头皮发麻的无奈感呢?不过,语法虽难,可你更强,只要我们下定心思好好干,这些对于我们肯定是完全不成问题的。之后呢,博主还会接着给大家带来类和对象剩下的内容,大家期待哦。

即使困难再大,他也决不低头,一定要坚持到底
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值