【C++指南】一文总结C++类和对象【上】

🌟 各位看官好,我是egoist2023

🌍 种一棵树最好是十年前,其次是现在!

🚀 今天来学习C++类和对象的语法知识

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

目录

类是什么

类的格式

访问限定符及类域

实例化对象

概念

对象大小

内存对齐规则

this指针

C语言的缺点

this指针的定义


类是什么

C++中 面向对象三大特性 : 封装、继承、多态 。今天学习类的相关知识,其是封装的体现之一。

类的格式

  • class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省 **略。**类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
cpp 复制代码
class Stack //关键字和名字
{
    //成员函数
	void Init()
	{
		//...
	}

private:
	// 成员变量
	int* _array;
	size_t _capacity;
	size_t _top;
}; //不能缺;
  • 为了区分成员变量,一般习惯上成员变量会加⼀个特殊标识,如成员变量前面或者后面加_ 或者m开头。
  • C++中,struct也可以定义类(兼容C中struct的用法),struct升级成了类,明显的变化是struct中可以定义函数,(class中内容默认是private 保护的,struct默认是public 公开的【可随意访问】)。
  • 定义在类里面的成员函数默认为inline 。(这样做的好处是如果某个函数需要频繁调用,编译器可选择性展开)

访问限定符及类域

  • C++中实现封装的方式,通过访问权限选择性的将其接口提供给外部的用户使用让对象更加完善。(即只要让客户知道怎么调用即可,并不需要了解里面的底层细节)
  • public修饰的成员在类外可以直接被访问private (protecded)修饰的成员在类外不能直接被访问 。访问权限直至出现下一个访问限定符为止,若没有直到类结束。
  • class默认为private,struct默认为public。
  • ⼀般成员变量都会被限制为private/protected,需要别人使用的成员函数会放为public。当然,如果有需要访问私有成员变量,可以通过调用函数接口访问私有成员变量(在类外无法直接访问,可在类内定义函数访问私有变量)
cpp 复制代码
#include<iostream>
using namespace std;

class Day
{
public:
	int GetYear() //在类里面访问私有成员变量
	{
		return _year;
	}
private:
	int _year = 1;
};

int main()
{
	Day d;//实例化
	cout << d.GetYear() << endl; //调用函数接口
	return 0;
}
  • 类也是⼀个新的作用域 ,类的所有成员都在类的作用域中,在类体外定义成员时,依然使用**:: 作用域操作符指明**成员属于哪个类域。
  • 类域影响的是编译的查找规则,若Init函数不指定作用域,那么编译时编译器默认从全局域寻找,找不到就会报错。指定类域就是知道Init是成员函数,当前域就会到类域中去查找。

实例化对象

概念

  • 用类类型在物理内存中创建对象的过程,称为类实例化出对象
  • 类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。(即只在实例化对象的时候才会开空间)
  • 一个类可以实例化出多个对象 ,实例化出的对象占用实际的物理空间,存储类成员变量。

看完是不是觉得很抽象,跟没看一样?打个比方

在日常生活中,房子是司空见惯的。而房子的设计需要设计图,即设计图 --> 房子

同理,在实例化对象当中,类就相当于是设计图(已经有人写好了,可供使用),而实例化对象就相当于房子。 即 类 --> 实例化对象

对象大小

既然类能实例化对象,开了空间意味着需要知道对象有多大,对象中肯定包含成员变量,同时要明白内存是如何对齐的。

内存对齐规则

• 第一个成员在与结构体偏移量为0的地址处。

• 对齐数 = 编译器默认的对齐数 与 该成员大小的较小值。(VS中默认的对齐数为8)
若要修改编译器默认对齐数,则添上如下代码

cpp 复制代码
#pragma pack(4)//设置默认对齐数为4
#pragma pack()//取消设置的对齐数

• 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
• 结构体总大小为:最大对齐数的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对⻬到自己的最⼤对⻬数的整数倍处,结构体的整体大小就是所有最大对⻬数(含嵌套结构体的对⻬数)的整数倍。
此结构体对象大小为多少呢?

cpp 复制代码
struct A
{
	int a;
	int b;
	char c;
};

根据内存对齐规则:第一个成员a在与结构体偏移量为0的地址处,占4个字节。成员b先对齐到对齐数的整数倍处,占4个字节;成员c同理。总占9个自己,由于需要对齐到最大对齐数的整数倍,因此结构体总大小为12。

那结构体内没有成员呢?

cpp 复制代码
struct A
{

};

运行后发现 A类对象的大小是1 ,为什么没有成员变量还要给1个字节呢?因为如果一个字节都不给,表示不了对象存在过,作用是为了占位标识对象存在。

疑惑 --> 为什么会存在内存对齐呢?

内存对齐是为了提高访问效率。

那么成员函数是否包含呢?不包含

了解:首先函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量存储各自的数据,但是d1和d2的成员函数Init指针却是一样的,存储在对象中就浪费了。如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。

这里需要再额外哆嗦⼀下,其实函数指针是不需要存储的,函数指针是⼀个地址,调用函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就需要存储函数地址。

this指针

C语言的缺点

在C语言版本中,在实现对应函数功能时,我们需要把对象的参数传过去,以便知道是哪个对象在调用对应函数。从下面程序中,每次都需要将对象的地址传过去,是非常繁琐和不方便的。因此在C++中引入了一个新的语法 --> this 指针。

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

class Date
{
public:
	//成员变量
	int _year;
	int _month;
	int _day;
};

void Init(Date* const d, int year, int month, int day)
{
	d->_year = year;
	d->_month = month;
	d->_day = day;
}

void Print(Date* const d)
{
	cout << d->_year << "-" << d->_month << "-" << d->_day << endl;
}

int main()
{
	Date d1;
	Date d2;
	Init(&d1,2025, 2, 28);
	Init(&d2,2024, 1, 14);

	Print(&d1);
	Print(&d2);
	return 0;
}

this指针的定义

• 编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加一个当前类类型的指针,叫做this 指针。

cpp 复制代码
void Init(int year,int month, int day)//隐藏this指针

其原型为

cpp 复制代码
void Init(Date* const this, int year,int month, int day)

这里补充一点,this指针加const的原因是为了使代码更加健壮,其目的是不改变this指针变量本身,可以改变this指针指向的内容,

• 类的成员函数中访问成员变量,本质都是通过this指针访问的。
• C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。

cpp 复制代码
void Init(int year, int month, int day)
{
    //显示传this指针
	this->_year = year;
	this->_month = month;
    this->_day = day;
}

设计出这个语法大大方便了程序员,将工作交给编译器来处理,并且使代码简洁且容易理解。如下便是C++版本中this指针作用的程序。

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

class Date
{
public:
    //成员函数
	void Init(int year,int month,int day)//隐藏的第一个参数为this指针
	{
        //this->_year=year;//可在函数内显示调用
		_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(2025, 2, 28);
	d2.Init(2024, 1, 14);

	d1.Print();
	d2.Print();
	return 0;
}

相关推荐
不会Hello World的小苗2 分钟前
Java——链表(LinkedList)
java·开发语言·链表
lsx20240614 分钟前
Perl 面向对象编程指南
开发语言
Allen Bright35 分钟前
【Java基础-46.3】Java泛型通配符详解:解锁类型安全的灵活编程
java·开发语言
JANGHIGH41 分钟前
c++ std::list使用笔记
c++·笔记·list
画个逗号给明天"1 小时前
C++STL容器之list
开发语言·c++
hrrrrb1 小时前
【Java】Java 常用核心类篇 —— 时间-日期API(上)
java·开发语言
小突突突1 小时前
模拟实现Java中的计时器
java·开发语言·后端·java-ee
七禾页话1 小时前
垃圾回收知识点
java·开发语言·jvm
web137656076431 小时前
Scala的宝藏库:探索常用的第三方库及其应用
开发语言·后端·scala
煤炭里de黑猫2 小时前
Lua C API :使用 lua_tonumber 函数从 Lua 栈中提取数值
开发语言·lua