C++类与对象(上)

在本章节的博客中,引入在C++中知名的类与对象的概念,同时将其和C语言中的结构体做对比,介绍类域对象的基本定义、

一.类的定义

类的定义关键字为class,使用方法和C语言中的结构体struct类似。

class 类名 {类的主体};如下面这段示例代码所示:

cpp 复制代码
class Date
{
public:
	//成员函数
	//成员函数默认为inline内联函数
	void DateInit(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//成员变量
	//为了区分成员变量,一般加上一个特殊标识
	int _year;
	int _month;
	int _day;
};

在这里定义了一个类名为Date的类,类中包含了一个初始化函数DateInit和三个整形成员变量_year _month _day。

从示例中可以明显看出C++中的类与C语言中结构体的不同之处:

1.类中可以定义函数,在类中所定义的函数称为成员函数或类的属性。类中定义的变量称为成员变量。

而类就是成员变量和对于该成员变量操作的成员函数整合到一起,形成一个集成化的操作接口,更为简洁的提供给用户进行IO操作。

2.类中存在访问限定符,就是上例代码中的public和private,以及没有出现过的protected。

在这里简述其区别,public限定符,表示该对象可以被外部(该类之外的地方,例如主函数中)访问;private和protected则表示该对象不可被外部访问。

如果在定义类时,不添加任何访问限定符,默认为private。

cpp 复制代码
include<iostream>
using namespace std;

class Date
{
public:
	//成员函数
	//成员函数默认为inline内联函数
	void DateInit(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;
	d1.DateInit(2026, 2, 20);
    d1.print();
	//d1._day //成员变量为private,不可访问
	return 0;
}

例如在这段代码中,在Date类的基础之上,在其内部多增加了一个打印函数。

主程序中先创建了一个Date类型的实例d1,然后调用类内成员函数DateInit对其进行初始化,之后调用成员函数Print对三个成员变量进行打印。可观察到输出结果。

但这里不能在main函数中,利用cout对类内成员_year _month _day进行打印,因为这三个成员变量是被private所限制。

错误类型为:error C2248: "Date::_day": 无法访问 private 成员(在"Date"类中声明)

仔细观察上述的错误类型,可以发现,出现了针对某个特定域的双引号操作符。这里继续引出概念,不同的类属于不同的域,其类似于全局域,主要目的是为了区分定义不同类型之间成员函数重名的情况。

如果成员函数的声明与定义分离,则其定义应该如下面的示例代码所写:

cpp 复制代码
class Date
{
public:
	void DateInit(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print();

	int _year;
	int _month;
	int _day;
};

void Date::Print()
{
    cout << _year << "/" << _month << "/" << _day << endl;
}

在类内对函数进行声明,然后类外进行函数定义的时候,要对其指定是哪个类域中的函数对象。

类内的成员函数默认为inline内联函数类型。

3.在C++中,同样支持C语言中的struct结构体操作,并且在C语言结构体的基础之上,和class类同样支持内部定义函数和访问限定符。

cpp 复制代码
struct Date1
{
public:
	//成员函数
	void DateInit(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//成员变量
	int _year;
	int _month;
	int _day;
};

如上述代码所示,其创建方法和类几乎一样,不同点是,struct定义的结构体如果不写访问限制符,则默认为public。

C++中定义的结构体不再需要typedef重命名。

二.实例化

class类只是对抽象变量的一个集合约束,其并没有在内存中开辟或占据实际空间,只有真正用这个类型创建了一块空间,才可以实际利用我们所创建的类型。

而这个过程,称为类的实例化 或 类实例化出对象。

一个类可以实例化出多个对象。

cpp 复制代码
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;
};

class A
{

};

class B
{

	void print()
	{
		cout << endl;
	}
};
int main()
{
	Date d1;
	d1.Init(2026, 2, 20);
	d1.Print();

	//该类的大小为12字节,为三个整型变量的空间,从此处可以看出,类中只存有变量,而不存储类内函数
	//为了节省空间,多次创建该类和调用相同函数时节省了这个相同函数的地址空间。
	//符合C语言结构体内存对齐的规则
	//内存对齐本质以空间换时间,为了可以一次读取到一类数据
	cout << sizeof(Date) << endl;


	//类A和B都没有任何变量,其理论大小应该为0,但实际输出却为1,这个1是占位标识符,为了表示这个类型的存在
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;

	return 0;
}

在这段代码中,创建了Date 、A和B这三种类,利用sizeof输出这三种类可以发现,Date的大小为12字节,恰好为三个整形变量遵循C语言中结构体内存对齐规则所计算出的大小。

所以,在这里引出C++中类的大小只计算类内成员变量的大小,而不计算类内成员函数的大小。其计算规则符合C语言中结构体内存对齐的规则。

那么为什么不在类中包含成员函数的大小呢?本质是为了节省空间,如果创建了100个利用该类实例化的对象,那么调用该函数的过程中就需要多存储100个完全一致的call指令(函数的地址为第一条指令的地址),函数地址完全相同,所以就不需要在类内对其进行存储。

(上述代码中VS默认对齐数为8)。

根据C语言中结构体的内存对齐规则,A和B的类内,没有任何成员变量,其理论大小应该是0,但是实际输出A和B的大小,发现为1。这个1,是类A B的占位标识符,为了证明该类存在。

三. this 指针

cpp 复制代码
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;
	}

	//void Print(Date* this)//编译器处理后会形成这样,但在写函数时不应该进行调用
	void Print()
	{
		cout << this << endl;//但是在类型内部可以进行调用
		cout << _year << "/" << _month << "/" << _day << endl;
		//cout << this->_year << "/" << this->_month << "/" <<this-> _day << endl;//本质是这样进行输出
	}

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

int main()
{
	Date d1;
	Date d2;
	d1.Init(2026, 2, 20);
	d2.Init(2025, 2, 20);

	d1.Print();
	d2.Print();
	
	//编译器对其进行了一定程度的处理,本质实际传参是:
	//d1.Print(&d1);
	//d2.Print(&d2);
	//但要注意,其不能在调用时进行传递 

	//this指针本质是函数传递的形参,存储在栈或寄存器

	return 0;
}

利用类实例化了两个对象d1 d2,分别对其进行初始化和打印数值的操作。但是并没有传递一些标识符类型的地址或引用,那么类是如何找到或区分这两个不同的对象呢?

这里引出this指针的概念,编译器会在编译过程中,默认给类内函数传递一个Date* const this类型的指针。

所以Init函数的参数在语法层面只需要写三个,而在编译器处理后本质就变成了:void Init(Date* const this ,int year, int month, int day)。

Print函数的参数就变成了:void Print(Date* this),实际调用的时候就变成了:

cout << this->_year << "/" << this->_month << "/" <<this-> _day << endl.

所以根据传递的this指针的不同来具体区分是哪一个实例化的对象。

但要注意的是,this指针不可以显示的进行参数的传递和函数形参的占位,但其可以在类内直接使用。

下面通过两个类的代码来加深对this指针的理解:

cpp 复制代码
class A
{
public:
	void Print()
	{
		cout << "A" << endl;
	}
private:
	int _a;
};

class B
{
public:
	void Print()
	{
		cout << "B" << endl;
		cout << _b << endl;
	}
private:
	int _b;
};

int main()
{

	A* p = nullptr;

	//此处,p为空指针,但是并不会报空指针错误。
	//本质原因是没有对p解引用,因为类的内置函数类型并不存储在类中,所以本质并不是对p解引用找到的类内函数
	//所以可以正常运行
	p->Print();
	//底下这个是相同的道理。
	(*p).Print();


	B* pb = nullptr;
	pb->Print();//这里就会产生空指针解引用的问题,因为在函数内部,进行了pb指针的this传参
	//打印_b本质是this->_b,空指针解引用为问题

	return 0;
}

首先创建了两个类型A和B,唯一不同点在于A中的print函数仅输出一个字符,而B的print函数还输出了类中定义的成员变量_b。

首先直接看主函数,创建了A类型的指针变量p赋值为空指针,然后利用p调用Print函数,这里第一次见到肯定会认为其是空指针调用从而使得程序报错。但实际运行可以发现,print函数可以正常打印出A。本质原因是p虽然是空指针,但并没有对其解引用,因为函数指针并没有存储在类内,所以就算p是空指针,也可以正常找到该函数的地址,从而打印出A。同理(*p).Print也是相同的道理。

但是如果B这样,虽然能正常找到Print函数,但是其内部需要具体输出_b,而输出_b的过程本质是this->_b ,this是空指针,这里就会形成空引用。从而导致程序崩溃。

最后针对this指针再说一个点,this指针本质是函数的形参,其存储在内存的栈区或者寄存器中。

最后总结一下C++的类:其具有面向对象的三大特征:封装、继承、多态。

C++可以将变量和对变量进行操作的函数都封装到类中,同时可以通过访问限定符限制某些变量的访问,从而规范化输入输出格式,避免随意访问修改。以及C++有着一些比较方便的语法,例如函数参数缺省以及this指针隐式传递。

相关推荐
十启树1 小时前
QGis开发环境部署
开发语言·gis·qgis
killer_queen48041 小时前
AI_agent(三) MCP协议(二)
c++·agent·mcp·a2a
亚比囧1 小时前
Java基础--面向对象(二)
java·开发语言
乐观勇敢坚强的老彭2 小时前
c++寒假营day05
开发语言·c++·算法
枫叶丹42 小时前
【Qt开发】Qt界面优化(七)-> Qt样式表(QSS) 样式属性
c语言·开发语言·c++·qt
重生之后端学习2 小时前
74. 搜索二维矩阵
开发语言·数据结构·算法·职场和发展·深度优先
@atweiwei2 小时前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
上海云盾-高防顾问2 小时前
DNS异常怎么办?快速排查+解决指南
开发语言·php
开发者小天2 小时前
python安装 Matplotlib 库 安装 Seaborn 库
开发语言·python·matplotlib