第一章:类与对象基础

说实话,我刚学C++那会儿,对"类"这个概念挺懵的。老师讲了一堆理论,什么"类是对象的抽象"、"对象是类的实例"……听得云里雾里。后来自己动手写代码,踩了几个坑,才慢慢明白过来。

今天咱们就来聊聊类与对象的基础。我会尽量用大白话讲,结合我实际项目中的经验,帮你把这些概念理清楚。

1. 类的定义与实例化

类是什么?说白了,就是你自己定义的一种数据类型。C++内置了int、double这些类型,但它们不够用啊。比如你要描述一个"学生",有姓名、年龄、成绩……这时候就需要自己造一个类型——这就是类。

看个最简单的例子:

class Student {
    // 这里写学生的属性和行为
};  // 注意这个分号,我当年漏写过无数次

定义好类之后,怎么用呢?创建对象,也叫实例化:

Student stu1;          // 栈上创建
Student* stu2 = new Student();  // 堆上创建

我个人习惯,小对象用栈,大对象或者需要控制生命周期的用堆。不过要记得,new出来的对象必须delete,否则内存泄漏——嗯,这个坑我踩过不止一次。

2. 访问修饰符:public、private、protected

这三个关键字,决定了类成员的"可见范围"。我刚开始总记混,后来用一句话就记住了:公开的随便用,私有的自己用,保护的给儿子用

修饰符 本类 子类 外部
public
protected
private

核心原则:成员变量尽量设为private,通过public的成员函数来访问。这叫"封装",是面向对象的基石。

我在项目中见过有人把所有成员都写成public,图省事。结果后来需求一变,到处都要改,那叫一个痛苦。你想想看,如果外部代码直接操作你的数据,你连改个变量名都得小心翼翼。

3. 成员变量与成员函数

成员变量就是类的属性,成员函数就是类的行为。举个例子:

class Student {
private:
    string name;   // 成员变量
    int age;
    float score;

public:
    void setName(string n) { name = n; }  // 成员函数
    string getName() { return name; }
};

这里name、age、score是成员变量,setName、getName是成员函数。注意看,我习惯把成员变量放在private里,然后通过public的getter/setter来访问。这样做的好处是:将来如果想加个校验逻辑,比如年龄不能为负,直接在setter里改就行,外部代码不用动。

小技巧:成员变量命名时,我习惯加个前缀m_,比如m_name、m_age。这样一眼就能看出是成员变量,不会和局部变量搞混。当然也有人用后缀下划线,看团队规范。

4. 构造函数与析构函数

构造函数,就是对象创建时自动调用的函数。用来做初始化工作。析构函数,是对象销毁时自动调用的函数。用来做清理工作。

class Student {
private:
    string name;
    int* scores;  // 动态数组

public:
    // 构造函数
    Student(string n, int count) {
        name = n;
        scores = new int[count];  // 分配内存
        cout << name << " 构造完成" << endl;
    }

    // 析构函数
    ~Student() {
        delete[] scores;  // 释放内存
        cout << name << " 析构完成" << endl;
    }
};

构造函数的名字和类名一样,没有返回值。析构函数前面加个~,也没有返回值。这两个函数如果你不写,编译器会帮你生成一个默认的——但默认的啥也不干。

注意:如果你的类里有指针成员,并且你new了内存,一定要在析构函数里delete。否则内存泄漏。我曾经在一个网络库中漏写了析构函数,服务器跑了三天后内存爆了……那教训,刻骨铭心。

构造函数可以重载,也就是说可以有多个不同参数的构造函数:

class Student {
public:
    Student() { name = "未知"; }           // 无参构造
    Student(string n) { name = n; }        // 带参构造
    Student(string n, int a) {             // 两个参数
        name = n;
        age = a;
    }
};

5. this指针

this是个隐藏的指针,指向当前对象本身。每个成员函数里都能用。什么时候需要显式用呢?最常见的情况——参数名和成员变量名冲突:

class Student {
private:
    string name;
public:
    void setName(string name) {
        this->name = name;  // 左边的name是成员变量,右边的name是参数
    }
};

你看,参数叫name,成员变量也叫name。如果不加this->,编译器就懵了:你到底要赋值给谁?加上this->,明确告诉它:给当前对象的name赋值。

还有一种用法,是在成员函数里返回当前对象本身:

class Student {
public:
    Student& setAge(int age) {
        this->age = age;
        return *this;  // 返回当前对象
    }
};

// 这样就可以链式调用了
stu.setAge(18).setName("张三");

这种写法在C++里叫"链式编程",很多库都用这种风格。我个人觉得挺酷的,但别滥用,否则代码可读性会下降。

知识体系总览

下面这张图,帮你把本章的知识点串起来:

类与对象基础 类的定义 • class 关键字 • 实例化:栈 vs 堆 访问修饰符 • public:公开 • private:私有 • protected:保护 成员变量与函数 • 属性 + 行为 • getter / setter 构造与析构 • 构造函数:初始化 • 析构函数:清理 • 重载与默认 this 指针 • 指向当前对象 • 解决命名冲突 • 链式调用

这张图把本章的五个核心知识点串在了一起。你可以看到,类的定义是基础,访问修饰符控制着成员的可见性,成员变量和函数构成了类的具体内容,构造和析构管理着对象的生命周期,而this指针则是成员函数里一个隐形的帮手。

好了,第一章的内容就到这里。记住一句话:类是蓝图,对象是房子。蓝图画得再好,也得动手盖起来才算数。多写代码,多踩坑,慢慢就熟练了。


公众号:蓝海资料掘金营,微信deep3321