C++作为一门支持面向对象编程的语言,其核心之一就是类和对象。掌握类的定义、访问控制、实例化机制以及this指针,是迈向C++进阶之路的关键一步。本文将从实战角度出发,带你系统梳理这些核心概念。

一、类的定义:自定义类型的基石
C++之父Bjarne Stroustrup在C语言的基础上引入了类(class)这一概念,让开发者能够创建属于自己的数据类型。与Python、Java等语言类似,C++的类将数据和操作数据的方法封装在一起,形成统一的整体。
定义一个类的基本语法如下:class关键字后跟类名,花括号{}内是类的主体,可以包含变量和函数,最后以分号;结尾。类中定义的变量称为成员变量(或成员属性),定义的函数称为成员函数(或成员方法)。
在实际开发中,为了区分成员变量和普通变量,通常会为成员变量添加特殊标识符,常见的有_a、a_、m_a等。具体风格取决于团队规范,本文示例统一采用_a的形式。
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
// 为了区分成员变量,⼀般习惯上成员变量
// 会加⼀个特殊标识,如_ 或者 m开头
int _year; // year_ m_year
int _month;
int _day;
};
值得一提的是,C++中的struct同样可以用来定义类,并且相比C语言有了重大升级——struct内部可以定义函数。使用时,struct后面的类名可以直接作为类型名使用,无需像C语言那样额外添加struct关键字。
// C++升级struct升级成了类
// 1、类⾥⾯可以定义函数
// 2、struct名称就可以代表类型
// C++兼容C中struct的⽤法
typedef struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
// 不再需要typedef,ListNodeCPP就可以代表类型
struct ListNodeCPP
{
void Init(int x)
{
next = nullptr;
val = x;
}
ListNodeCPP* next;
int val;
};
⚠️ 需要注意的是,定义在类内部的成员函数,编译器默认将其视为内联函数(inline),这有助于减少函数调用的开销。
二、访问限定符与类域:封装的守护者
类的引入同时带来了类域的概念。你可以把类域想象成一个独立的封闭空间,与全局域、局部域之间存在天然的壁垒。要实现空间之间的交互,就需要特定的通道或令牌。
C++提供了三个访问限定符:
- public:公开访问,相当于授权的通道,任何代码都可以访问
- private:私有访问,相当于禁地,只有类内部可以访问
- protected:保护访问,介于两者之间,主要用于继承场景
每个访问限定符的作用范围从它出现的位置开始,到下一个访问限定符或类体结束(})为止。被private和protected修饰的成员,外部无法直接访问,但可以通过作用域访问操作符(::)在特定场景下访问。
class Stack
{
public:
// 成员函数
void Init(int n = 4);//声明
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)//定义
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
类域对编译器的查找规则有重要影响。默认情况下,编译器在编译时只会在全局域和局部域中查找标识符,不会主动进入类域。只有当明确指定类域或提供访问权限时,编译器才会进入类域进行查找。
这一机制与Java、TypeScript等语言的访问控制理念相通,但C++的实现更加底层和灵活。如果你熟悉Go语言的包级别访问控制,会发现C++的类域控制更加细粒度。
[AFFILIATE_SLOT_1]
三、实例化:从图纸到实体
用自定义类类型创建对象的过程称为实例化。类本身就像一张建筑图纸,描述了对象的结构和行为,但图纸本身不能住人。只有根据图纸建造出的房子——也就是对象——才具有实际的使用价值。
✅ 同一个类可以实例化出多个对象,就像同一张图纸可以建造多栋相同户型的房子。类中的成员变量只是声明,并不分配实际内存空间,只有在实例化对象时才会为成员变量分配空间。


关于对象大小,需要明确以下几点:
- 每个对象都拥有独立的成员变量存储空间
- 成员函数不包含在对象中,它们存储在代码段,编译后变成汇编指令
- 对象大小遵循与C语言结构体相同的内存对齐规则
- 即使是空类,对象大小也为1字节,用于标记该类型的存在
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
return 0;
}


为什么空类的对象大小是1而不是0?因为如果为0,编译器将无法区分该对象是否真实存在,1字节仅作为占位标记。
四、this指针:成员函数的隐形翻译官
既然成员函数存储在公共代码区,那么当多个对象调用同一个成员函数时,函数如何知道操作的是哪个对象的数据?答案就是this指针。
this指针是编译器自动添加到成员函数中的第一个形参,类型为当前类类型的指针。当对象调用成员函数时,编译器会将对象的地址作为this指针传入。在成员函数内部访问成员变量时,本质上都是通过this指针完成的。
⚠️ 需要注意:this指针是隐式的,不能在形参或实参中显式声明。它只在类内部可用,外部无法直接访问。这种设计让成员函数能够优雅地操作调用对象的数据,而无需开发者手动传递对象地址。
#include
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
//Date* const this,这个是限定了指针指向的地址不可修改,指向的内容可修改
void Init(int year, int month, int day)
{
// 编译报错:error C2106: “=”: 左操作数必须为左值
// this = nullptr;
// this->_year = year;
_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(&d1, 2024, 3, 31);
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
理解this指针对于掌握C++的面向对象编程至关重要。它与JavaScript中的this、Python中的self有相似之处,但C++的this是隐式传递的,而Python的self需要显式声明。
this指针本质是形参,是局部变量。
五、C++与C语言实现Stack的对比
通过对比C和C++实现栈(Stack)的方式,可以更直观地感受面向对象编程的优势。C语言需要手动管理数据结构和函数之间的关系,而C++通过类将数据和操作封装在一起,代码更加清晰、安全。
类的学习可以让我们感受一下封装~
C++中数据和函数都放到了类⾥⾯,通过访问限定符进⾏了限制,不能再随意通过对象直接修改数 据,这是C++封装的⼀种体现,这个是最重要的变化。这⾥的封装的本质是⼀种更严格规范的管 理,避免出现乱访问修改的问题。C++中有⼀些相对⽅便的语法,⽐如Init给的缺省参数会⽅便很多,成员函数每次不需要传对象地 址,因为this指针隐含的传递了,⽅便了很多,使⽤类型不再需要typedef⽤类名就很⽅便。
总结来说,C++的类机制提供了更好的封装性、代码复用性和可维护性。相比C语言,C++在实现数据结构时能够显著减少出错概率,提升开发效率。
[AFFILIATE_SLOT_2]
总结
本文系统介绍了C++类和对象的核心知识:类的定义语法与成员变量/函数、三种访问限定符与类域规则、实例化机制与对象内存布局、this指针的工作原理。这些概念是C++面向对象编程的基础,理解它们对于后续学习继承、多态等高级特性至关重要。建议读者动手实践文中示例代码,加深理解。
#include
using namespace std;
// 计算⼀下A/B/C实例化的对象是多⼤?
class A
{
public:
void Print()
{
cout << _ch << endl;
}
private:
char _ch;
int _i;
};
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;
}
#include
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print(); //p是一个类类型对象的指针,Print()是类中的成员函数,并不在对象里面,
//所以就不存在对空指针的解引用,算是调用
return 0;
}
#include
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
cout << _a << endl; //实则出现了对空指针的解引用
// cout << this->_a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}

浙公网安备 33010602011771号