【C++ Primer】第十五章:面向对象程序设计

面向对象程序设计是 C++ 的核心章节,涵盖了继承、多态、动态绑定等关键概念。这是理解 C++ 面向对象编程精髓的关键。

1. 继承基础

基本继承语法

// 基类
class Quote {
private:
    std::string bookNo;  // 私有成员,派生类不能访问
protected:
    double price;        // 受保护成员,派生类可以访问
public:
    Quote() = default;
    Quote(const std::string& book, double sales_price)
        : bookNo(book), price(sales_price) {}
    
    std::string isbn() const { return bookNo; }
    
    // 虚函数:派生类可以覆盖
    virtual double net_price(std::size_t n) const {
        return n * price;
    }
    
    // 虚析构函数:确保正确释放派生类对象
    virtual ~Quote() = default;
};

// 派生类
class Bulk_quote : public Quote {  // public 继承
private:
    std::size_t min_qty = 0;      // 享受折扣的最低购买量
    double discount = 0.0;        // 折扣额
public:
    Bulk_quote() = default;
    Bulk_quote(const std::string& book, double p, 
               std::size_t qty, double disc)
        : Quote(book, p), min_qty(qty), discount(disc) {}
    
    // 覆盖基类的虚函数
    double net_price(std::size_t n) const override {
        if (n >= min_qty) {
            return n * (1 - discount) * price;
        } else {
            return n * price;  // 没有折扣
        }
    }
};

继承中的访问控制

class Base {
public:
    int public_member;
protected:
    int protected_member;
private:
    int private_member;  // 派生类不能访问
};

class Public_Derived : public Base {
    // public_member 仍然是 public
    // protected_member 仍然是 protected
    // private_member 不可访问
};

class Protected_Derived : protected Base {
    // public_member 变为 protected
    // protected_member 仍然是 protected
    // private_member 不可访问
};

class Private_Derived : private Base {
    // public_member 变为 private
    // protected_member 变为 private
    // private_member 不可访问
};

2. 虚函数和多态

动态绑定机制

void print_total(std::ostream& os, const Quote& item, std::size_t n) {
    // 动态绑定:在运行时根据 item 的实际类型调用正确的 net_price
    os << "ISBN: " << item.isbn() 
       << " # sold: " << n 
       << " total due: " << item.net_price(n) << std::endl;
}

void polymorphism_demo() {
    Quote basic("123-456-789", 20.0);
    Bulk_quote bulk("123-456-789", 20.0, 10, 0.1);  // 10本以上打9折
    
    print_total(std::cout, basic, 15);  // 调用 Quote::net_price
    print_total(std::cout, bulk, 15);   // 调用 Bulk_quote::net_price
    // 输出:
    // ISBN: 123-456-789 # sold: 15 total due: 300
    // ISBN: 123-456-789 # sold: 15 total due: 270
}

虚函数表(vtable)概念

class Base {
public:
    virtual void func1() { std::cout << "Base::func1" << std::endl; }
    virtual void func2() { std::cout << "Base::func2" << std::endl; }
    void func3() { std::cout << "Base::func3" << std::endl; }  // 非虚函数
};

class Derived : public Base {
public:
    void func1() override { std::cout << "Derived::func1" << std::endl; }
    void func3() { std::cout << "Derived::func3" << std::endl; }  // 隐藏基类版本
};

void vtable_concept() {
    Base* bp = new Derived();
    bp->func1();  // 动态绑定:调用 Derived::func1(通过 vtable)
    bp->func2();  // 动态绑定:调用 Base::func2(通过 vtable)
    bp->func3();  // 静态绑定:调用 Base::func3(编译时确定)
    
    delete bp;
}

3. 抽象基类

纯虚函数和抽象基类

// 抽象基类:包含纯虚函数
class Shape {
protected:
    std::string name;
public:
    Shape(const std::string& n) : name(n) {}
    virtual ~Shape() = default;
    
    // 纯虚函数:派生类必须实现
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    
    // 普通虚函数:派生类可以选择性覆盖
    virtual void print() const {
        std::cout << "Shape: " << name << std::endl;
    }
    
    // 非虚函数:派生类继承
    const std::string& get_name() const { return name; }
};

// 具体派生类
class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r, const std::string& n = "Circle") 
        : Shape(n), radius(r) {}
    
    // 必须实现纯虚函数
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    double perimeter() const override {
        return 2 * 3.14159 * radius;
    }
    
    // 可以选择性覆盖普通虚函数
    void print() const override {
        std::cout << "Circle: " << name << ", radius: " << radius << std::endl;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h, const std::string& n = "Rectangle")
        : Shape(n), width(w), height(h) {}
    
    double area() const override {
        return width * height;
    }
    
    double perimeter() const override {
        return 2 * (width + height);
    }
};

4. 访问控制和继承

公有、保护、私有继承

class Base {
public:
    void pub_func() {}
protected:
    void prot_func() {}
private:
    void priv_func() {}
};

// 公有继承:接口继承
class PublicDerived : public Base {
public:
    void test() {
        pub_func();     // OK:公有继承,保持 public
        prot_func();    // OK:派生类可以访问 protected
        // priv_func(); // 错误:不能访问 private
    }
};

// 保护继承:实现继承
class ProtectedDerived : protected Base {
public:
    void test() {
        pub_func();     // OK:但变为 protected
        prot_func();    // OK:仍然是 protected
    }
};

// 私有继承:实现继承,不暴露接口
class PrivateDerived : private Base {
public:
    void test() {
        pub_func();     // OK:但变为 private
        prot_func();    // OK:但变为 private
    }
};

void access_control_demo() {
    PublicDerived d1;
    d1.pub_func();      // OK
    
    ProtectedDerived d2;
    // d2.pub_func();   // 错误:在派生类中变为 protected
    
    PrivateDerived d3;
    // d3.pub_func();   // 错误:在派生类中变为 private
}

5. 构造函数和析构函数在继承中的行为

构造函数调用顺序

class Base {
public:
    Base() { std::cout << "Base constructor" << std::endl; }
    Base(int x) { std::cout << "Base(" << x << ") constructor" << std::endl; }
    virtual ~Base() { std::cout << "Base destructor" << std::endl; }
};

class Derived : public Base {
public:
    // 默认调用 Base(),然后执行 Derived 构造函数体
    Derived() { std::cout << "Derived constructor" << std::endl; }
    
    // 显式调用基类构造函数
    Derived(int x, int y) : Base(x) {
        std::cout << "Derived(" << y << ") constructor" << std::endl;
    }
    
    ~Derived() { 
        std::cout << "Derived destructor" << std::endl;
        // 自动调用 ~Base()
    }
};

void constructor_demo() {
    std::cout << "Creating d1:" << std::endl;
    Derived d1;
    std::cout << "\nCreating d2:" << std::endl;
    Derived d2(10, 20);
    std::cout << "\nDestroying objects:" << std::endl;
}
// 输出:
// Creating d1:
// Base constructor
// Derived constructor
//
// Creating d2:
// Base(10) constructor
// Derived(20) constructor
//
// Destroying objects:
// Derived destructor
// Base destructor
// Derived destructor
// Base destructor

6. 纯虚函数和抽象基类实践

#include <vector>
#include <memory>

class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    virtual void draw() const = 0;
    virtual std::unique_ptr<Shape> clone() const = 0;  // 原型模式
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    double perimeter() const override {
        return 2 * 3.14159 * radius;
    }
    
    void draw() const override {
        std::cout << "Drawing Circle with radius " << radius << std::endl;
    }
    
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);
    }
};

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() const override {
        return width * height;
    }
    
    double perimeter() const override {
        return 2 * (width + height);
    }
    
    void draw() const override {
        std::cout << "Drawing Rectangle " << width << "x" << height << std::endl;
    }
    
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Rectangle>(*this);
    }
};

// 使用多态容器
void shape_system_demo() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(5.0));
    shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));
    
    // 多态行为
    for (const auto& shape : shapes) {
        shape->draw();
        std::cout << "Area: " << shape->area() 
                  << ", Perimeter: " << shape->perimeter() << std::endl;
    }
    
    // 克隆演示
    auto original = std::make_unique<Circle>(10.0);
    auto cloned = original->clone();
    std::cout << "Original area: " << original->area() << std::endl;
    std::cout << "Cloned area: " << cloned->area() << std::endl;
}

7. 类型转换和继承

动态类型转换

class Base {
public:
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    void derived_method() {
        std::cout << "Derived specific method" << std::endl;
    }
};

void type_conversion_demo() {
    Base* bp = new Derived();
    
    // 向下转换:Base* -> Derived*
    Derived* dp = dynamic_cast<Derived*>(bp);
    if (dp) {  // 转换成功
        dp->derived_method();
    } else {
        std::cout << "Dynamic cast failed" << std::endl;
    }
    
    // 引用转换
    try {
        Derived& dr = dynamic_cast<Derived&>(*bp);
        dr.derived_method();
    } catch (const std::bad_cast& e) {
        std::cout << "Bad cast: " << e.what() << std::endl;
    }
    
    delete bp;
}

8. 面向对象设计原则

Liskov 替换原则

// 好的设计:正方形是矩形的特例,但要注意行为一致性
class Rectangle {
protected:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    virtual void set_width(double w) { width = w; }
    virtual void set_height(double h) { height = h; }
    
    double area() const { return width * height; }
};

class Square : public Rectangle {
public:
    Square(double side) : Rectangle(side, side) {}
    
    void set_width(double w) override {
        width = height = w;  // 保持正方形特性
    }
    
    void set_height(double h) override {
        width = height = h;  // 保持正方形特性
    }
};

void lsp_demo() {
    Rectangle* rect = new Square(5);
    rect->set_width(10);     // 正确设置正方形的边长
    std::cout << "Area: " << rect->area() << std::endl;  // 100
    
    delete rect;
}

最佳实践总结

  1. 对多态基类声明虚析构函数
  2. 使用 public 继承表示 “is-a” 关系
  3. 通过引用或指针传递多态类型
  4. 抽象基类定义接口,具体类实现行为
  5. 谨慎使用多重继承
  6. 使用 override 关键字明确表示覆盖虚函数

常见陷阱

class Base {
public:
    virtual void func() { std::cout << "Base" << std::endl; }
};

class Derived : public Base {
public:
    // 错误:拼写错误,创建了新函数而不是覆盖
    void func(int x) { std::cout << "Derived" << std::endl; }
    
    // 正确:使用 override 编译器会检查
    void func() override { std::cout << "Derived" << std::endl; }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值