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]

三、实例化:从图纸到实体

用自定义类类型创建对象的过程称为实例化。类本身就像一张建筑图纸,描述了对象的结构和行为,但图纸本身不能住人。只有根据图纸建造出的房子——也就是对象——才具有实际的使用价值。

✅ 同一个类可以实例化出多个对象,就像同一张图纸可以建造多栋相同户型的房子。类中的成员变量只是声明,并不分配实际内存空间,只有在实例化对象时才会为成员变量分配空间。

关于对象大小,需要明确以下几点:

  1. 每个对象都拥有独立的成员变量存储空间
  2. 成员函数不包含在对象中,它们存储在代码段,编译后变成汇编指令
  3. 对象大小遵循与C语言结构体相同的内存对齐规则
  4. 即使是空类,对象大小也为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;
}