🔷 第十章 类和对象
📄 一句话概括

类成员 = 成员变量 + 成员函数,但除了这两个"普通居民",类里还有几个特殊身份的成员:内联成员函数(写在类里的小函数,就地展开)、静态成员(属于类、不属于某个对象,所有对象共享一份)、隐藏的 this 指针(成员函数里那个"当前对象"的地址,是"成员函数怎么知道在操作谁"的答案)。此外还有两种"住在里面的类":嵌套类(类里定义的类)和局部类(函数里定义的类)。本节先把地图铺开,后面六节逐个说清。

📝 本节和前面几节的关系

10.1~10.3 讲完了"对象的一生"(造出来、复制、销毁)。本节开始回头看细节:类里面到底有哪些东西、它们各自有哪些脾气。这一组会反复用到你已经很熟的老知识——static(6.7.2)、内联(6.6)、作用域(6.4),只是把它们从"函数里"搬到"类里"。你会发现:类里没有新概念,只有"同样概念的新位置"。

一、成员的两大阵营

阵营 成员 关键特点
每个对象各有一份 普通成员变量 a.x 和 b.x 是两块不同的内存(10.1.1)
整个类只有一份 静态成员变量(10.4.3) Point::count,所有对象共享
跟着对象走 普通成员函数(含内联、const 成员函数) 必须用对象来调:p.show()
不跟对象走 静态成员函数(10.4.3) 用类名就能调:Point::getCount()

一句话记住这个分界:

"这个成员,是要'每个对象一份',还是'全类共用一份'?" 前者是普通成员,后者是静态成员。

二、本节路线图

小节 讲什么 一句话
10.4.1 访问类成员 怎么碰成员 对象用 .、指针用 ->;类内互相调不用带对象名;const 成员函数的用法
10.4.2 内联成员函数 写在类里的函数 默认就是内联——就地展开,省一次函数调用
10.4.3 静态类成员 属于类不属于对象 static 成员变量要在类外定义;静态成员函数没有 this
10.4.4 隐藏的 this 指针 "当前对象"是谁 成员函数偷偷摸摸收了一个"自己的地址"参数,这就是 this
10.4.5 嵌套类 类里定义的类 像"抽屉里的抽屉",外面要写 Outer::Inner
10.4.6 局部类 函数里定义的类 只在那个函数里能用的类,几乎不用,认得就行

三、先建立直觉:类成员就像"公司里的职位"

类里的东西 公司里的对应
普通成员变量 每个员工的私人储物柜——各有一份
静态成员变量 公司公告栏——全公司就一块,谁都能看
普通成员函数 员工能做的事——必须"某个员工"去做
const 成员函数 只查看不改动的事(查工资条不算修改记录)
静态成员函数 公司层面的事(比如"统计公司总人数"),不需要指定某个员工
this 指针 "我正在办的是谁的事" 这个自知道
嵌套类 部门里的内部小组——外面要写"某部门的小组"才提得到

有了这个直觉,后面六节读起来会顺很多。

四、动手 Demo:四种成员各来一个

#include <iostream>
using namespace std;

class Company {
private:
    int salary;                        // 普通成员:每人一份
    static int total;                  // 静态成员:全公司一份(10.4.3)

public:
    Company(int s) : salary(s) {
        total += s;                    // 静态成员可以直接用
    }

    // 普通成员函数:必须用对象调
    void showSalary() const {          // const 成员函数:承诺不改数据(10.4.1)
        cout << "我的工资 = " << salary << endl;
    }

    // 静态成员函数:不需要对象也能调
    static int getTotal() {            // 注意:没有 this,不能碰 salary
        return total;
    }
};

int Company::total = 0;                // ★ 静态成员必须在类外"定义"一次(10.4.3)

int main() {
    Company a(3000);
    Company b(5000);

    a.showSalary();                    // 我的工资 = 3000
    b.showSalary();                    // 我的工资 = 5000

    cout << "全公司总工资 = " << Company::getTotal() << endl;   // 8000,用类名调
    cout << "也能用对象调 = " << a.getTotal() << endl;           // 8000
    return 0;
}

看到什么算成功:

我的工资 = 3000
我的工资 = 5000
全公司总工资 = 8000
也能用对象调 = 8000

读结果:

  1. a 和 b 的 salary 互不干扰(3000 / 5000)——普通成员每人一份。
  2. total 两个对象共建共享(8000)——静态成员全类一份。
  3. getTotal() 可以用 Company:: 直接调——静态成员函数不依赖某个对象。

注意最后一行那个"也能用对象调":直接用 Company::getTotal() 更清楚,说明这个函数和具体对象无关。看到能这么调的成员,就知道它是 static 的。

五、常见误区

  1. "类成员只有成员变量和成员函数"——分类上是的,但成员函数又有好几个变体(const 成员函数、内联、静态),成员变量也有(静态)。把它们当成"同一类东西的不同性格",而不是新概念。
  2. "静态成员也是每个对象一份"——不是。静态成员属于类,全类只有一份(10.4.3 细讲)。判据:所有对象看到的必须是同一个值吗? 是,就用 static。
  3. "这些高级特性都要用上才算会写类"——完全相反。日常写的类,九成只用到普通成员 + 构造函数 + 析构函数。静态成员、嵌套类这些是在特定问题上才需要的工具。先明白"什么时候需要",比记住语法更重要。
  4. "const 成员函数和 const 成员变量是一回事"——不是。const 成员变量指"这个数据不能改"(10.2.1 的初始化列表);const 成员函数指"这个函数承诺不修改对象的数据"(10.4.1)。一个管数据,一个管函数。
自测三问(点开对答案) 1. 类成员分两大阵营,怎么判?——问一句:"**这个成员是每个对象一份,还是全类共用一份?**" 前者是**普通成员**(普通成员变量、普通成员函数),后者是**静态成员**(`static`)。 2. 静态成员函数和普通成员函数,调用方式有什么不同?——**普通成员函数必须用对象调**(`p.show()`);**静态成员函数可以用类名直接调**(`Point::getCount()`),因为它不依赖具体对象。 3. `const` 成员函数是在形容什么?——形容**"这个函数不会修改对象的数据"**(它是一个承诺)。**对象是 `const` 时,只能调用 `const` 成员函数**(10.4.1 讲原因)。

相关笔记