Skip to content

C++ 第10课:类的大小、继承与权限控制

课程目标:

  • 理解 C++ 中类的大小计算方式
  • 掌握类的继承机制,区分基类和派生类
  • 理解不同继承方式对成员访问权限的影响
  • 学会使用 protected 和 final 关键字控制成员访问和继承

1. sizeof(自定义类)

类的大小指的是该类型对象所占用的内存空间大小。

cpp
class MyClass {
public:
    int publicVar;
    void publicFunc() {}

private:
    int privateVar;
    static int staticVar;
};

int main() {
    MyClass obj;
    cout << sizeof(obj) << endl; // 输出 8,因为只计算两个 int 成员变量的大小
    cout << sizeof(MyClass) << endl; // 输出 8,与 sizeof(obj) 相同
    return 0;
}

要点:

  • 类的大小与其对象的大小一致。
  • 类的大小是所有非静态成员变量大小之和,与成员函数和静态成员变量无关。
  • 类成员的构造顺序:先执行构造函数初始化列表,同时执行父类构造函数,最后构造成员。
  • 类内部编译顺序:先编译所有成员变量,然后编译成员函数。
  • 类外部编译顺序:按照代码行顺序编译。
  • 内存对齐:为了提高 CPU 访问效率,成员变量会按照 4 字节对齐,不足 4 的倍数时会进行填充。

2. 类的继承

继承是面向对象编程中一个重要的概念,允许我们创建一个新类(派生类)基于现有类(基类),从而实现代码复用和扩展。

cpp
// 基类
class Animal {
public:
    string name;
    Animal(string n) : name(n) {}
    void eat() { cout << name << " is eating." << endl; }
};

// 派生类
class Bird : public Animal {
public:
    int wingspan;
    Bird(string n, int w) : Animal(n), wingspan(w) {}
    void fly() { cout << name << " is flying." << endl; }
};

int main() {
    Bird sparrow("Sparrow", 20);
    sparrow.eat();  // 调用基类的成员函数
    sparrow.fly();  // 调用派生类的成员函数
    return 0;
}

要点:

  • 子类继承父类,拥有父类所有非私有成员。
  • 可以多层次继承,形成继承链。
  • 父类的私有成员在子类中无法访问,但受保护的成员可以访问。
  • 继承方式:
    • 公有继承 (public):父类成员的访问权限在子类中保持不变。
    • 私有继承 (private):父类所有成员在子类中都变成私有权限。
  • 函数重写:子类可以定义与父类同名函数,实现功能的覆盖。
  • 子类成员可以覆盖父类的同名成员,无论是函数还是变量。

3. protected 访问权限

protected 关键字用于定义受保护的成员,这些成员只能在类内部和派生类中访问,无法从外部访问。

cpp
class Animal {
protected:
    int age;
};

class Dog : public Animal {
public:
    void setAge(int a) { age = a; } // 可以访问 protected 成员
};

int main() {
    Dog myDog;
    // myDog.age = 5;  // 错误,无法直接访问 protected 成员
    myDog.setAge(5); 
    return 0;
}

要点:

  • protected 权限用于保护成员,限制其访问范围。
  • 私有继承和保护继承会缩小继承成员的访问权限。
  • 公有继承保持继承成员的访问权限不变。
  • 基类的 private 成员在派生类中无法访问。

4. final 关键字

final 关键字可以用于阻止类被继承,或者阻止虚函数被重写。

cpp
class Shape {
public:
    virtual void draw() = 0;
};

class Circle : public Shape {
public:
    void draw() final { /* 实现 */ }  // final 阻止该函数被重写
};

//class SubCircle : public Circle { ... };  // 错误,Circle 类被 final 修饰,无法被继承

要点:

  • 使用 final 修饰的类无法被继承。
  • 使用 final 修饰的虚函数无法被重写。

课后作业:设计一个简单的员工管理系统

目标:

  • 运用所学的类、对象和继承的知识,设计一个简单的员工管理系统。
  • 体现面向对象编程中代码复用和扩展的优势。

需求:

  1. 员工类 (Employee):
    • 基类,包含员工的共同属性(例如姓名、工号、部门等),以及计算薪水 (calculateSalary) 的函数(可设置为返回固定值或简单计算)。
  2. 经理类 (Manager):
    • 继承自 Employee 类,增加特有属性:管理津贴 (bonus)。
    • 重写 calculateSalary 函数,计算方式为:员工基础薪水 + 管理津贴。
  3. 销售员类 (Salesperson):
    • 继承自 Employee 类,增加特有属性:销售额 (salesVolume) 和提成比例 (commissionRate)。
    • 重写 calculateSalary 函数,计算方式为:员工基础薪水 + 销售额 * 提成比例。

提示:

  • 思考共同特征和区别,将其抽象成类和成员。
  • 使用合适的访问权限控制成员的可见性和可访问性。
  • 可以使用构造函数初始化列表为成员变量賦值。
  • 编写测试代码验证类的功能和继承关系。