作为 C++ 初学者,刚接触类与对象时总觉得云里雾里:为啥要抛弃熟悉的 C 语言结构体?类到底是个啥?对象和类又是什么关系?这篇笔记我会用最接地气的语言、最易懂的例子,把类与对象的核心知识点掰开揉碎讲清楚,既是学习记录,也是复习神器。
📚 目录
1.🤔 先唠唠:面向过程 vs 面向对象(为啥 C++ 更香?)
2.💡 类的由来:结构体的 "升级版"(C++ 对 C 的温柔改造)
3.✍️ 类的定义:手把手教你写第一个类(避坑指南)
4.🔒 封装的秘密:访问限定符(给代码 "上锁")
5.📌 类的作用域:别让代码 "迷路"
6.🧱 类的实例化:把设计图变成实物(类≠对象!)
7.📏 算大小:类的对象占多少内存?(新手必踩坑)
8.🕵️ this 指针:隐藏的 "对象身份证"(核心中的核心)
9.🆚 实战对比:C 语言栈 vs C++ 栈(看封装的威力)
10. 核心总结:必背的 5 个关键点
1. 🤔 先唠唠:面向过程 vs 面向对象(为啥 C++ 更香?)
刚学 C++ 时,我总问自己:"C 语言好好的,为啥要学类和对象?" 直到用 C 写了个洗衣机模拟程序,才懂了差距 ------
1.1 面向过程(C 语言):一步一步 "指挥"
核心是 "按步骤做事",就像手动洗衣服:拿盆 → 放水 → 放衣服 → 倒洗衣粉 → 搓洗 → 换水 → 拧干 → 晾衣服每一步都要我们写代码 "指挥",哪怕只是想换个洗衣粉,都要改一堆步骤代码,麻烦又容易错。
新手体验:用 C 写程序,就像亲手做一桌满汉全席,从买菜、切菜到炒菜全要自己来,少一步都不行。
1.2 面向对象(C++):找 "对象" 帮忙
核心是 "找对象干活",就像用洗衣机洗衣服:我们只需要找 3 个 "对象"------ 人、洗衣机、衣服。人只需要按洗衣机的 "启动键"(接口),不用管洗衣机内部怎么搓、怎么甩干,洗衣机自己会搞定。
体验:用 C++ 写程序,就像点外卖,告诉商家(对象)要啥菜(调用接口),不用管后厨怎么做,坐等结果就行~
核心差异表

2. 💡 类的由来:结构体的 "升级版"(C++ 对 C 的温柔改造)
C++ 是 C 的超集,所以先从熟悉的结构体入手~C 语言的结构体只能装变量,比如定义一个栈:
c
// C语言结构体:只有变量,没函数
typedef int DataType;
struct Stack
{
DataType* _array; // 数组
size_t _capacity; // 容量
size_t _size; // 元素个数
};
// 操作栈的函数只能写在外面
void StackInit(struct Stack* ps)
{
// 初始化逻辑
}
但 C++ 给结构体加了 "buff"------ 能在里面写函数了
cpp
// C++结构体:既能装变量,也能装函数
typedef int DataType;
struct Stack
{
// 直接在结构体里写初始化函数
void Init(size_t capacity)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (_array == nullptr)
{
perror("malloc失败"); // 新手提示:一定要检查内存申请!
return;
}
_capacity = capacity;
_size = 0;
}
// 成员变量
DataType* _array;
size_t _capacity;
size_t _size;
};
不过 C++ 更推荐用class代替struct定义类 ------ 这才是面向对象的核心载体~
3. ✍️ 类的定义:手把手教你写第一个类(避坑指南)
3.1 基本语法(必记)
cpp
class 类名
{
// 成员:变量(属性)+ 函数(方法)
}; // 踩坑点:分号千万别漏!漏了编译器报错能懵半天
class:定义类的关键字,记死!
类名:比如Person、Date,首字母大写(编程规范);
成员:变量叫 "成员变量"(比如人的姓名、年龄),函数叫 "成员函数"(比如人的吃饭、走路)。
3.2 两种定义方式
方式 1:声明 + 定义全写在类里(适合简单函数)
cpp
class Person
{
public: // 访问限定符,后面会讲
// 成员函数:显示信息(新手提示:类里写函数,编译器可能当成内联函数)
void ShowInfo()
{
cout << "姓名:" << _name << " 年龄:" << _age << endl;
}
// 成员变量:加下划线区分形参(新手必备技巧!)
string _name; // 姓名
int _age; // 年龄
};
点评:写起来快,适合练手,但函数多了会乱,实际项目少用。
方式 2:声明放.h,定义放.cpp(实战推荐)
这是公司里最常用的写法,越早习惯越好!
第一步:新建person.h(头文件,只写声明)
cpp
// person.h
#include <string>
using namespace std; // 新手提示:头文件里尽量少用using,这里为了简化
class Person
{
public:
// 只声明,不写具体逻辑
void ShowInfo();
// 成员变量
string _name;
int _age;
};
第二步:新建person.cpp(源文件,写定义)
cpp
// person.cpp
#include "person.h"
#include <iostream>
// 新手重点:必须加「类名::」,告诉编译器这是Person类的函数
void Person::ShowInfo()
{
cout << "姓名:" << _name << " 年龄:" << _age << endl;
}
优势:代码结构清晰,后期改逻辑只动.cpp,不用动.h,不容易出错。
3.3 必避坑:成员变量命名
刚学的时候我总犯这错:形参和成员变量重名,导致赋值无效!
cpp
// 反例:坑哭新手
class Date
{
public:
void Init(int year)
{
year = year; // 编译器分不清哪个是形参,哪个是成员变量!
}
private:
int year;
};
正确写法(推荐):给成员变量加前缀 _
cpp
class Date
{
public:
void Init(int year)
{
_year = year; // 一眼分清!
}
private:
int _year; // 加下划线
};
4. 🔒 封装的秘密:访问限定符(给代码 "上锁")
封装是面向对象的三大特性之一 (另外两个是继承、多态,下篇讲),可以理解为:"把不想让人碰的东西锁起来,只留能用的接口 "。
4.1 三个访问限定符

重点:
1.权限从限定符出现的位置开始,到下一个限定符结束;
2.class默认private(不写限定符,成员全锁起来),struct默认public(兼容 C 语言);
3.这些限定符只在编译时有用,代码运行后,内存里没有 "锁" 的区别~
4.2 封装的本质
封装不是 "藏起来不让看",而是 "管理起来更安全"。比如手机:我们只需要用屏幕、按键(public接口),不用碰内部的电池、主板(private成员)------ 就算想换电池,也得通过官方维修点(指定接口),不会自己拆坏。
代码演示
cpp
class Phone
{
public:
// 对外接口:开机、打电话
void PowerOn() { cout << "手机开机" << endl; }
void Call(string number) { cout << "拨打:" << number << endl; }
private:
// 内部成员:电池、主板(类外碰不到)
int _battery; // 电池电量
string _board; // 主板型号
};
int main()
{
Phone myPhone;
myPhone.PowerOn(); // 能调用,public
myPhone.Call("10086"); // 能调用,public
// myPhone._battery = 100; // 编译报错!private,类外不能访问
return 0;
}
4.3 面试题:struct 和 class 的区别
默认权限:struct默认public,class默认private;
兼容性:struct能当 C 的结构体用,class不行;
继承时:struct默认公有继承,class默认私有继承(下篇讲)。
5. 📌 类的作用域:别让代码 "迷路"
可以把类的作用域理解为 "类的专属地盘"------ 类里的成员,只有在这个 "地盘" 里才能直接用,出了地盘就得 "报家门"。
cpp
class Person
{
public:
// 声明:在Person的地盘里,直接写函数名就行
void PrintInfo();
private:
string _name = "小明";
int _age = 18;
};
// 定义:出了Person的地盘,必须加「Person::」报家门
void Person::PrintInfo()
{
// 函数里在Person地盘内,直接用成员变量
cout << _name << " " << _age << endl;
}
提醒 :如果漏写Person::,编译器会以为这是个全局函数,找不到_name和_age,直接报错!
6. 🧱 类的实例化:把设计图变成实物(类≠对象!)
这是新手最容易混淆的点:类只是 "设计图",对象才是 "按图做出来的实物"。
6.1 核心区别(比喻)
类:建筑设计图(只画了房子长啥样,没有实际房子,不占空间);
对象:按设计图盖的房子(有实际空间,能住人,占内存)。
cpp
class Person
{
public:
string _name;
int _age;
};
int main()
{
// 错误:类没有内存,不能直接赋值!
// Person._age = 20;
// 正确:实例化对象(盖房子),对象才有内存
Person p1;
p1._age = 20; // 给对象的成员变量赋值
return 0;
}
6.2 一个类能造多个对象(示例)
cpp
void Test()
{
// 实例化2个Person对象,各自有独立的内存
Person p1; // 小明
p1._name = "小明";
p1._age = 18;
Person p2; // 小红
p2._name = "小红";
p2._age = 17;
}
理解:就像一张设计图,能盖 10 栋、100 栋房子,每栋房子都有自己的家具(成员变量),但建造方法(成员函数)是一样的。
7. 📏 算大小:类的对象占多少内存?(必踩坑)
新手第一次算类对象大小,总会疑惑:"类里有函数有变量,为啥大小只算变量?"
7.1 存储规则(秒懂)
C++ 编译器很聪明,为了节省内存 :
成员变量:每个对象独立存(因为每个对象的属性不一样,比如小明 18 岁,小红 17 岁);
成员函数:所有对象共享(因为吃饭、走路的方法都一样,没必要每个对象存一份)。
所以:对象大小 = 成员变量总大小 + 内存对齐补齐的字节(和结构体对齐规则一样)。
cpp
// 类1:有成员变量+成员函数
class A1 {
public:
void f1() {} // 函数不占对象内存
private:
int _a; // int占4字节,无对齐
};
// 类2:只有成员函数
class A2 {
public:
void f2() {}
};
// 类3:空类(啥都没有)
class A3 {};
int main()
{
cout << sizeof(A1) << endl; // 输出4(只有int _a的大小)
cout << sizeof(A2) << endl; // 输出1(编译器给的标识字节,区分不同对象)
cout << sizeof(A3) << endl; // 输出1(空类也需要1字节标识)
return 0;
}
7.3 结构体对齐规则(复习)
VS 编译器默认对齐数是 8,规则记这 4 点:
1.第一个成员从偏移量 0 的位置开始;
2.其他成员对齐到「对齐数」的整数倍(对齐数 = min (编译器默认值,成员大小));
3.总大小是最大对齐数的整数倍;
4.嵌套结构体时,嵌套的部分对齐到自己的最大对齐数,整体大小是所有最大对齐数的整数倍。
cpp
class A {
private:
char _a; // 1字节
int _b; // 4字节
double _c; // 8字节
};
// 计算:_a(1) + 补齐3字节 + _b(4) + _c(8) = 16字节(最大对齐数8,16是8的倍数)
cout << sizeof(A) << endl; // 输出16
8. 🕵️ this 指针:隐藏的 "对象身份证"(核心中的核心)
新手理解 this 指针的关键:"成员函数怎么知道操作的是哪个对象?"
8.1 this 指针的由来(场景)
看这段代码,d1 和 d2 都调用 Init 函数,为啥 d1 的年份是 2022,d2 是 2023?
cpp
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year, _month, _day;
};
int main()
{
Date d1, d2;
d1.Init(2022, 1, 1);
d2.Init(2023, 1, 1);
return 0;
}
答案:编译器给每个非静态成员函数加了个隐藏的 this 指针参数,指向当前调用函数的对象。
8.2 编译器的 "小动作"(可视化)
你写的代码:
cpp
void Date::Init(int year, int month, int day)
{
_year = year;
}
编译器实际处理的代码:
cpp
// 偷偷加了this指针参数
void Date::Init(Date* this, int year, int month, int day)
{
this->_year = year; // 偷偷用this访问成员变量
}
你调用的代码:
cpp
d1.Init(2022,1,1);
编译器实际调用的代码:
cpp
Date::Init(&d1, 2022,1,1); // 偷偷传对象地址给this
8.3 this 指针的特性(必记)
1.类型:类名* const(比如Date* const)------this 指针的指向不能改(不能给 this 赋值);
2.作用域:只能在成员函数内部用;
3.存储位置:是形参,存在栈区(编译器优化后可能用寄存器传递);
4.新手易错点:this 可以为空,但访问成员变量会崩溃!
8.4 实战:this 为空的两种情况
情况 1:this 为空,不访问成员变量→正常运行
cpp
class A
{
public:
void Print()
{
cout << "我只是打印一句话,不碰成员变量" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr; // p是空指针
p->Print(); // 正常运行:不用访问成员变量,无需解引用this
return 0;
}
情况 2:this 为空,访问成员变量→崩溃
cpp
class A
{
public:
void PrintA()
{
cout << _a << endl; // 本质是this->_a,this为空,解引用崩溃
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA(); // 运行崩溃:空指针解引用(新手必记!)
return 0;
}
9. 🆚 实战对比:C 语言栈 vs C++ 栈(看封装的威力)
对比着写一遍,就能深刻理解 C++ 的优势!
9.1 C 语言实现栈(体验:麻烦又易错)
c
#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Stack
{
DataType* _array;
size_t _capacity;
size_t _size;
};
// 所有函数都要传Stack*,还要手动检查是否为空
void StackInit(struct Stack* ps)
{
if (ps == NULL) return; // 新手容易忘写这行!
ps->_array = (DataType*)malloc(sizeof(DataType)*4);
ps->_capacity = 4;
ps->_size = 0;
}
void StackPush(struct Stack* ps, DataType x)
{
if (ps == NULL) return;
// 扩容逻辑(新手容易写错)
if (ps->_size == ps->_capacity)
{
ps->_capacity *= 2;
ps->_array = (DataType*)realloc(ps->_array, ps->_capacity*sizeof(DataType));
}
ps->_array[ps->_size++] = x;
}
int main()
{
struct Stack s;
StackInit(&s); // 必须传地址,新手容易漏&
StackPush(&s, 1);
StackPush(&s, 2);
return 0;
}
9.2 C++ 实现栈(体验:简洁又安全)
cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef int DataType;
class Stack
{
public:
// 不用传Stack*,编译器自动传this
void Init()
{
_array = (DataType*)malloc(sizeof(DataType)*4);
_capacity = 4;
_size = 0;
}
void Push(DataType x)
{
// 扩容逻辑,直接用成员变量,不用ps->_
if (_size == _capacity)
{
_capacity *= 2;
_array = (DataType*)realloc(_array, _capacity*sizeof(DataType));
}
_array[_size++] = x;
}
private:
// 成员变量私有化,类外不能乱改,更安全
DataType* _array;
size_t _capacity;
size_t _size;
};
int main()
{
Stack s;
s.Init(); // 直接调用,不用传地址
s.Push(1);
s.Push(2);
// s._size = 100; // 编译报错!private,防止新手乱改
return 0;
}
9.3 总结:C++ 香在哪?
*1.不用手动传Stack ,编译器用 this 搞定;
2.成员变量私有化,防止误操作;
3.调用函数更简单,不用写&s;
4.代码更整洁,数据和方法绑在一起。**
10. 📝 核心总结(必背)
1.类是 "设计图",对象是 "实物",只有对象占内存,大小仅算成员变量(含对齐);
2.封装是 "管理" 不是 "隐藏",通过访问限定符控制权限,让代码更安全;
3.this 指针是隐藏形参,指向当前对象,为空时访问成员变量会崩溃;
4.class默认private,struct默认public,核心区别在默认权限;
5.C++ 相比 C,把数据和方法封装在一起,代码更简洁、更安全,新手入门稍难但后期更香。
