大家好,我是“我想吃余”,很高兴你能和我一起进入到C++的学习中,我会将我的学习过程中的宝贵经验不遗余力的输入到文章中,希望可以帮助到你的学习。本文涵盖了从面向过程与面向对象的区别,到类的定义、访问限定符、封装、作用域、实例化、对象大小计算,以及this指针等内容。
举个洗衣服的例子:手洗衣服的步骤

机洗衣服:

核心区别:面向对象通过对象交互隐藏细节,提高代码复用性和可维护性。
C语言的结构体:只能定义成员变量。
C++的类(class/struct):可以定义成员变量和成员函数。
// C++实现栈(使用class)
class Stack {
public:
void Init() { /* 初始化逻辑 */ }
void Push(int data) { /* 压栈逻辑 */ }
private:
int* _array;
int _size;
};class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类的两种定义方式:

showlnfo函数可能会被编译器当成内联函数
.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
💡:这是更推荐的定义方式,因为可以提高代码可读性和编译效率。
成员变量命名规则的建议:
在给成员变量命名时,在其前面加一个_或者一个m
原因:避免与函数形参重命名导致的可读性差的问题。
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

public修饰的成员在类外可以直接被访问protected/private修饰的成员在类外不能直接被访问} 即类结束class的默认访问权限为private,struct为public(因为struct要兼容C)💡:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
面向对象的三大特性:封装、继承、多态。
什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
其实封装本质上是一种管理,让用户更方便使用类。
封装的意义:将数据与操作结合,隐藏实现细节,仅暴露接口。
class Date {
public:
void SetYear(int year) { _year = year; } // 对外提供接口
private:
int _year; // 隐藏实现细节
};举两个例子:

2. 计算机:对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::作用域操作符指明成员属于哪个类域。
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " "<< _gender << " " << _age << endl;
}实例化:类像“设计图”,对象是“具体建筑”。
Person p; // 实例化对象
p._name = "Tom"; // 对象占用实际内存用类类型创建对象的过程,称为类的实例化
💡:定义一个类,并没有给其对象开辟内存空间,实例化才是定义对象和开辟内存空间
一个十分形象的比方:类是一张“房子的设计图”,对象是一座“房子”,实例化就是“用设计图造一座房子”

计算方式与计算结构体大小方法相同,都是用结构体对齐来计算的。
对象大小计算规则:
内存对齐规则:
class A {
char _a; // 1字节(对齐到1)
int _b; // 4字节(对齐到4)
}; // 总大小:8字节(1+3填充+4)具体计算方法不过多赘述了,存有疑问的话建议阅读我过去的文章:高阶C语言|和结构体与位段的邂逅之旅
作用:隐式指向调用成员函数的对象,解决“如何区分不同对象”的问题。
现在看不懂没关系,我们来看一段代码:
我们定义一个日期类 Date
#include<iostream>
using namespace std;
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}对于上述类,我们会有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题: C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
如果将this指针显示出来,其实是这样的:
#include<iostream>
using namespace std;
class Date
{
public:
void Init(Date* const this, int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
void Print(Date* const this)
{
cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
d1.d1.Init(&d1, 2024, 3, 31);
d1.Print(&d1);
d2.Init(&d2, 2024, 7, 5);
d2.Print(&d2);
return 0;
}

类型* const ,即成员函数中,不能给this指针赋值。ecx寄存器自动传递,不需要用户传递💡: this不能在形参和实参显示传递,但是可以在函数内部显示使用
面向对象的核心在于封装,通过类将数据与方法结合,隐藏细节并提供接口。理解this指针、内存对齐和访问控制是掌握C++类与对象的关键。