一、类与对象:从结构体到面向对象的跨越

我记得刚学C语言那会儿,写个学生管理系统,得把姓名、学号、成绩全塞进结构体里。然后呢?操作这些数据的函数散落在各个角落,维护起来那叫一个痛苦。后来接触了C++的类,才恍然大悟——原来代码可以这么组织。

说白了,类就是你的自定义数据类型,而对象是这个类型的具体实例。就像「学生」是一个类,「张三」就是一个对象。

1.1 定义一个类

先看一个最简单的例子。我习惯把类的定义放在头文件里,实现放在cpp里,但初学阶段写在一起也没问题。

class Student {
public:  // 先别管这个,后面会讲
    // 成员变量
    string name;
    int age;
    float score;

    // 成员函数
    void showInfo() {
        cout << "姓名: " << name << ", 年龄: " << age 
             << ", 成绩: " << score << endl;
    }
};  // 注意这个分号,我见过有人漏掉它编译报错半天

1.2 创建对象

类定义好了,怎么用?创建对象呗。有两种方式:

int main() {
    // 栈上创建(推荐,自动管理生命周期)
    Student stu1;
    stu1.name = "小明";
    stu1.age = 18;
    stu1.score = 92.5;
    stu1.showInfo();

    // 堆上创建(需要手动释放)
    Student* stu2 = new Student();
    stu2->name = "小红";
    stu2->showInfo();
    delete stu2;  // 别忘了!

    return 0;
}
我的习惯:能用栈就别用堆。除非对象生命周期需要跨函数传递,或者对象特别大。堆上new出来的对象,忘记delete就是内存泄漏——我曾经在一个服务器项目里排查了三天,最后发现是new了没delete。

二、访问修饰符:public、private、protected

为什么要有访问修饰符?你想想看,你银行卡的余额能随便让别人看吗?不能。类的设计也是这个道理——把该暴露的暴露,该隐藏的隐藏

2.1 三种修饰符的作用

修饰符 类内部 派生类 外部
public 可访问 可访问 可访问
protected 可访问 可访问 不可访问
private 可访问 不可访问 不可访问

嗯,这里要注意:class的默认访问权限是private,而struct是public。这是C++里class和struct唯一的区别。

2.2 实际应用场景

我建议你这样设计类:

  • 成员变量一律private——通过public的getter/setter访问
  • 对外接口用public——别人怎么用你的类
  • 内部辅助函数用private——你自己调用的,别人不需要知道
  • protected留给继承用——子类需要但外部不需要的
class BankAccount {
private:
    double balance;  // 余额必须私有,不能让人随便改

public:
    // 公开的接口
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    double getBalance() {
        return balance;  // 只读,不能直接改
    }

protected:
    // 子类可能需要访问的
    string accountNumber;
};
我曾经踩过的坑:把成员变量设为public,结果项目里到处都在直接修改它。后来要加个校验逻辑,得把所有调用点找出来改一遍,那叫一个酸爽。所以——永远把成员变量设为private,这是血的教训。

三、成员函数:类的行为

成员函数就是类能做的事情。它和普通函数最大的区别是:它知道自己是哪个对象在调用

3.1 定义成员函数

可以在类内部直接定义,也可以在类外定义。我个人的习惯是:简单的函数写在类里,复杂的写在类外

class Rectangle {
private:
    double width;
    double height;

public:
    // 类内定义(隐式inline)
    void setSize(double w, double h) {
        width = w;
        height = h;
    }

    // 类外声明
    double getArea();
};

// 类外定义
double Rectangle::getArea() {
    return width * height;
}

3.2 this指针

每个成员函数都有一个隐藏参数——this指针。它指向调用该函数的对象本身。

class Person {
private:
    string name;

public:
    void setName(string name) {
        // 这里参数名和成员变量名冲突了
        // this->name 指的是成员变量
        // name 指的是参数
        this->name = name;
    }
};

说白了,this就是「我自己」的指针。当参数名和成员变量名相同时,必须用this->来区分。

3.3 const成员函数

如果一个成员函数不会修改对象的状态,我建议你把它声明为const。这既是承诺,也是保护。

class Circle {
private:
    double radius;

public:
    double getRadius() const {  // 这个函数不会修改radius
        return radius;
    }

    void setRadius(double r) {  // 这个函数会修改
        radius = r;
    }
};
小技巧:const对象只能调用const成员函数。如果你写了一个函数接收const引用参数,它只能调用该对象的const方法。所以——只读的成员函数都加上const,这是好习惯。

四、知识体系总览

下面这张图把本章的核心知识点串起来了。我画图的时候习惯把「类」放在中间,因为它是一切的基础。

类 (Class) 自定义数据类型 成员变量 描述对象的状态 成员函数 描述对象的行为 访问修饰符 控制访问权限 public 谁都能访问 private 只有自己能访问 protected 自己和子类能访问 对象 (Object) 类的具体实例 实例化

五、总结与最佳实践

聊了这么多,我总结几条实战经验:

  1. 类名用大写开头,对象名用小写开头——这是约定俗成的,别搞反了
  2. 成员变量一律private,通过public的getter/setter访问——封装性的核心
  3. 只读成员函数加const——既是文档也是约束
  4. 能用栈对象就别用堆对象——省心省力,避免内存泄漏
  5. 类定义末尾别忘了分号——我见过太多人栽在这个小细节上

面向对象的基础就这些。说白了,类就是把数据和操作数据的方法打包在一起。你把这个思想理解了,后面的继承、多态学起来就顺了。

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