C++继承第二讲--实现一个不可继承的类,友元在继承当中的陷阱,菱形继承解析

C++ 继承进阶:不可继承类、友元陷阱与菱形继承解析

标签:C++ | 面向对象 | 继承 | 多态 | 组合
难度:中级
阅读时间:约 15 分钟


摘要

在掌握 C++ 继承的基本概念后,我们往往会遇到一些进阶问题:如何禁止类被继承?友元关系能否继承?静态成员在继承体系中如何分布?多重继承的菱形问题怎么解决?继承和组合到底该选哪个?

本文系统讲解继承体系的进阶知识点,涵盖不可继承类的设计、友元与静态成员的特性、菱形继承问题的本质及虚继承解决方案,以及继承与组合的设计哲学对比。每节均配有完整代码示例和避坑提醒,帮助读者建立扎实的面向对象设计基础。

读完本文,你能回答这些问题:

  • 如何将一个类设计为"不可继承"?
  • 为什么基类的友元无法访问派生类的私有成员?
  • 静态成员在继承体系中是共享的还是独立的?
  • 菱形继承是什么问题?虚继承如何解决?
  • 继承和组合的本质区别是什么?何时该用哪个?

适合谁读:已掌握 C++ 继承基础概念,想深入理解继承细节和设计原则的开发者
前置知识:类与对象、访问限定符、构造函数与析构函数、基本继承语法


一、实现一个不能被继承的类

1.1 为什么需要禁止继承?

在实际开发中,有些类设计上就不应该被继承,比如:

  • 工具类:提供静态方法,无需继承扩展
  • 安全关键类:防止派生类破坏封装性或安全保证
  • 最终实现类:类的设计已经完成,不允许进一步修改

1.2 方法一:私有化构造函数

原理:派生类构造时必须调用基类构造函数,如果基类构造函数是私有的,派生类无法访问,编译失败。

#include <iostream>
using namespace std;

class Person {
private:
    // 私有构造函数,外部无法直接调用
    Person(const string& name, int age) 
        : _name(name), _age(age) {
        cout << "Person 构造" << endl;
    }
    
protected:
    string _name;
    int _age;
    
public:
    // 提供工厂方法创建对象
    static Person create(const string& name, int age) {
        return Person(name, age);
    }
    
    void show() const {
        cout << "Name: " << _name << ", Age: " << _age << endl;
    }
};

// 尝试继承会编译失败
// class Student : public Person {  // ❌ 错误!基类构造函数私有
// public:
//     int _stuId;
// };

int main() {
    Person p = Person::create("张三", 20);
    p.show();
    return 0;
}

⚠️ 注意:私有构造函数方案需要配合工厂方法或友元类使用,否则连本类都无法创建对象。

1.3 方法二:使用 final 关键字(C++11 推荐)

原理:C++11 引入 final specifier,直接告诉编译器该类不可被继承。

#include <iostream>
using namespace std;

// 使用 final 标记类,禁止继承
class Person final {
public:
    Person(const string& name, int age) 
        : _name(name), _age(age) {
        cout << "Person 构造" << endl;
    }
    
protected:
    string _name;
    int _age;
    
public:
    void show() const {
        cout << "Name: " << _name << ", Age: " << _age << endl;
    }
};

// 尝试继承会编译失败
// class Student : public Person {  // ❌ 错误!类被标记为 final
// public:
//     int _stuId;
// };

int main() {
    Person p("张三", 20);
    p.show();
    return 0;
}

1.4 两种方法对比

特性私有构造函数final 关键字
C++ 标准C++98 即可C++11 引入
代码简洁性需要额外工厂方法直接标记,简洁
意图明确性较隐晦非常明确
灵活性可配合友元/工厂方法完全禁止继承
推荐程度传统方案现代 C++ 首选

💡 最佳实践:优先使用 final 关键字,语义清晰且不易出错。仅在需要兼容旧标准或需要更精细控制时才使用私有构造函数方案。


二、继承与友元

2.1 核心结论:友元关系不能继承

重要规则:基类的友元函数或友元类,无权访问派生类的私有/保护成员

#include <iostream>
using namespace std;

class Base {
    friend void friendFunc(Base&);  // 友元函数
    
protected:
    int _baseX = 10;
};

class Derived : public Base {
private:
    int _derivedY = 20;
};

// 友元函数只能访问 Base 的成员
void friendFunc(Base& b) {
    cout << "Base member: " << b._baseX << endl;  // ✅ 合法
    
    // 如果传入的是 Derived 对象呢?
    // cout << b._derivedY << endl;  // ❌ 错误!友元关系不继承
}

int main() {
    Derived d;
    friendFunc(d);  // 编译通过,但只能访问 Base 部分
    return 0;
}

2.2 常见误区

误区:很多人认为友元具有"传递性",即基类的友元可以访问派生类的成员。

事实:友元是特殊的授权关系,仅限于声明它的类。派生类是独立的类,其私有成员不会被基类的友元自动授权访问。

class Base {
    friend void accessAll();
private:
    int _baseOnly = 1;
};

class Derived : public Base {
private:
    int _derivedOnly = 2;
};

void accessAll() {
    Base b;
    b._baseOnly = 10;  // ✅ 合法
    
    Derived d;
    d._derivedOnly = 20;  // ❌ 错误!友元不能访问派生类私有成员
}

2.3 如何让友元访问派生类成员?

有几种方式:

方式一:在派生类中也声明友元

class Derived : public Base {
    friend void accessAll();  // 重新声明友元
private:
    int _derivedOnly;
};

方式二:使用公有接口访问

class Derived : public Base {
public:
    int getDerivedOnly() const { return _derivedOnly; }
private:
    int _derivedOnly;
};

方式三:将函数声明为两个类的友元

class Base {
    friend void accessAll();
};

class Derived : public Base {
    friend void accessAll();  // 也可以访问
};

三、继承与静态成员

3.1 核心规则:静态成员在继承体系中唯一

重要结论:基类定义的 static 静态成员,在整个继承体系中只有一个实例,所有派生类共享。

#include <iostream>
using namespace std;

class Person {
public:
    static int count;  // 声明静态成员
    
    Person() {
        ++count;
        cout << "Person 构造,当前人数: " << count << endl;
    }
};

// 定义并初始化静态成员
int Person::count = 0;

class Student : public Person {
public:
    Student() {
        cout << "Student 构造,当前人数: " << count << endl;
    }
};

class Teacher : public Person {
public:
    Teacher() {
        cout << "Teacher 构造,当前人数: " << count << endl;
    }
};

int main() {
    cout << "初始人数: " << Person::count << endl;  // 0
    
    Student s1;   // 调用 Person 构造函数
    Student s2;
    Teacher t1;
    
    cout << "最后人数: " << Person::count << endl;  // 3
    cout << "Student::count: " << Student::count << endl;  // 3(同一个)
    cout << "Teacher::count: " << Teacher::count << endl;  // 3(同一个)
    
    // 验证地址相同
    cout << "&Person::count: " << &Person::count << endl;
    cout << "&Student::count: " << &Student::count << endl;
    cout << "&Teacher::count: " << &Teacher::count << endl;
    // 三个地址完全相同!
    
    return 0;
}

3.2 内存模型说明

内存布局示意:

┌─────────────────────────────────────┐
│         Person 类静态数据区          │
│  ┌─────────────────────────────┐    │
│  │         count: 3            │    │
│  └─────────────────────────────┘    │
│         ↑ 所有类共享这一个地址       │
└─────────────────────────────────────┘

对象实例:
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│   Student s1  │  │   Student s2  │  │   Teacher t1  │
│   (无独有数据) │  │   (无独有数据) │  │   (无独有数据) │
└──────────────┘  └──────────────┘  └──────────────┘
      ↑                   ↑                  ↑
      └───────────────────┴──────────────────┘
                        │
                  共享 Person::count

3.3 注意事项

注意点说明
必须定义一次静态成员需要在类外定义和初始化(C++17 可用 inline static
访问方式灵活可用 类名::成员对象.成员 访问
生命周期静态成员在程序启动时创建,结束时销毁,与对象无关
线程安全多线程环境下需注意静态成员的并发访问问题

四、多重继承与菱形继承问题

4.1 三种继承模型

(1)单继承
class Person {};
class Student : public Person {};          // Student is a Person
class PostGraduate : public Student {};    // PostGraduate is a Student

结构:链式结构,简单清晰。

(2)多重继承
class Person {};
class Student : public Person {};
class Teacher : public Person {};
class Assistant : public Student, public Teacher {};  // 同时继承两个基类

结构:一个派生类有多个直接基类。

(3)菱形继承(Diamond Inheritance)
        Person
       /      \
  Student    Teacher
       \      /
        Assistant

问题演示

#include <iostream>
#include <string>
using namespace std;

class Person {
protected:
    string _name;
    int _age;
    
public:
    Person(const string& name, int age) 
        : _name(name), _age(age) {}
    
    void show() const {
        cout << "Name: " << _name << ", Age: " << _age << endl;
    }
};

class Student : public Person {
public:
    Student(const string& name, int age, int id)
        : Person(name, age), _stuId(id) {}
    
    void show() const {
        Person::show();
        cout << "Student ID: " << _stuId << endl;
    }
    
private:
    int _stuId;
};

class Teacher : public Person {
public:
    Teacher(const string& name, int age, string title)
        : Person(name, age), _title(title) {}
    
    void show() const {
        Person::show();
        cout << "Title: " << _title << endl;
    }
    
private:
    string _title;
};

// ❌ 菱形继承问题
class Assistant : public Student, public Teacher {
public:
    Assistant(const string& name, int age, int id, string title)
        : Student(name, age, id), Teacher(name, age, title) {}
    
    // 问题:name 有歧义!
    void printName() {
        // cout << _name << endl;           // ❌ 编译错误:二义性
        cout << Student::_name << endl;     // ✅ 指定访问 Student 部分
        cout << Teacher::_name << endl;     // ✅ 指定访问 Teacher 部分
    }
};

4.2 菱形继承的两个问题

问题一:二义性(Ambiguity)
Assistant a("张三", 25, 1001, "助教");

// ❌ 编译错误:_name 在 Assistant 中有两份
// cout << a._name << endl;

// ✅ 必须指定访问哪一份
cout << a.Student::_name << endl;  // 访问 Student 继承的 _name
cout << a.Teacher::_name << endl;  // 访问 Teacher 继承的 _name
问题二:数据冗余(Redundancy)
Assistant 对象内存布局(菱形继承,无虚继承):

┌─────────────────────────────────────────────┐
│                 Assistant 对象               │
├─────────────────┬─────────────────┬─────────┤
│   Student 部分   │   Teacher 部分   │ 独有成员 │
│                 │                 │         │
│  ┌───────────┐  │  ┌───────────┐  │         │
│  │ Person部分 │  │  │ Person部分 │  │         │
│  │ _name     │  │  │ _name     │  │         │
│  │ _age      │  │  │ _age      │  │         │
│  └───────────┘  │  └───────────┘  │         │
│  _stuId         │  _title         │         │
└─────────────────┴─────────────────┴─────────┘
      ↑                      ↑
   两份 Person 成员!        数据冗余

4.3 解决方案:虚继承(Virtual Inheritance)

原理:使用 virtual 关键字修饰继承,确保公共基类在最终派生类中只有一份实例。

#include <iostream>
#include <string>
using namespace std;

class Person {
protected:
    string _name;
    int _age;
    
public:
    Person(const string& name, int age) 
        : _name(name), _age(age) {
        cout << "Person 构造: " << name << endl;
    }
    
    void show() const {
        cout << "Name: " << _name << ", Age: " << _age << endl;
    }
};

// ✅ 使用虚继承
class Student : virtual public Person {
public:
    Student(const string& name, int age, int id)
        : Person(name, age), _stuId(id) {
        cout << "Student 构造" << endl;
    }
    
private:
    int _stuId;
};

class Teacher : virtual public Person {
public:
    Teacher(const string& name, int age, string title)
        : Person(name, age), _title(title) {
        cout << "Teacher 构造" << endl;
    }
    
private:
    string _title;
};

// ✅ 菱形继承问题被解决
class Assistant : public Student, public Teacher {
public:
    // 注意:虚继承时,最派生类必须直接初始化虚基类
    Assistant(const string& name, int age, int id, string title)
        : Person(name, age),    // ✅ 必须显式调用 Person 构造
          Student(name, age, id),
          Teacher(name, age, title) {
        cout << "Assistant 构造" << endl;
    }
    
    void printInfo() {
        // ✅ 无歧义,只有一份 _name
        cout << "Name: " << _name << endl;
        cout << "Student ID: " << _stuId << endl;
        cout << "Title: " << _title << endl;
    }
};

int main() {
    Assistant a("张三", 25, 1001, "助教");
    a.printInfo();
    return 0;
}

4.4 虚继承的内存布局

Assistant 对象内存布局(使用虚继承):

┌─────────────────────────────────────────────┐
│              Assistant 对象                  │
├─────────────────────────────────────────────┤
│  虚基类指针 (vptr) → 指向唯一 Person 实例    │
├─────────────────┬─────────────────┬─────────┤
│   Student 部分   │   Teacher 部分   │ 独有成员 │
│                 │                 │         │
│  [共享 Person]  │  [共享 Person]  │         │
│   _name         │   _name         │         │
│   _age          │   _age          │         │
│  _stuId         │  _title         │         │
└─────────────────┴─────────────────┴─────────┘
         ↑
    只有一份 Person 成员!

4.5 虚继承的注意事项

注意点说明
最派生类负责初始化虚基类由最终派生类直接初始化,而非中间类
性能开销虚继承引入虚基类指针,增加内存和访问开销
复杂度增加理解和管理虚继承的成本较高
设计建议尽量避免复杂的多继承,优先使用接口继承(纯虚类)

4.6 为什么 Java 等语言不支持多继承?

“C++ 被认为复杂的原因之一是多继承,后来的很多编程语言都没有多继承,如 Java。”

Java 选择单继承 + 接口实现的方式,避免了菱形继承的复杂性:

  • 单继承:一个类只有一个直接父类
  • 接口多实现:一个类可以实现多个接口
  • 接口方法默认实现(Java 8+):通过 default 方法提供备选实现

这种设计简化了语言规范,降低了学习成本,但也牺牲了一定的灵活性。


五、继承与组合:设计哲学

5.1 核心概念对比

特性继承 (is-a)组合 (has-a)
关系语义“是一个”“拥有一个”
耦合度
封装性破坏(暴露基类实现细节)保持(黑盒复用)
灵活性差(编译期绑定)好(运行时替换)
扩展性受限于层次结构可自由组合
适用场景类型层次清晰、行为可复用行为复杂、需灵活装配

5.2 白箱复用 vs 黑箱复用

白箱复用(继承)
// ❌ 白箱复用:基类内部细节对派生类可见
class Engine {
public:
    void start() { cout << "Engine starting..." << endl; }
    // 派生类可以看到并使用 start() 的内部实现
};

class Car : public Engine {  // Car is an Engine
public:
    void drive() {
        start();  // 直接使用基类实现
    }
};

缺点

  • 基类实现变更可能影响所有派生类
  • 派生类与基类耦合紧密
  • 难以在运行时替换行为
黑箱复用(组合)
// ✅ 黑箱复用:被组合对象的内部细节不可见
class Engine {
public:
    void start() { cout << "Engine starting..." << endl; }
};

class Car {
public:
    void drive() {
        _engine.start();  // 通过接口调用,不关心内部实现
    }
    
private:
    Engine _engine;  // Car has an Engine
};

优点

  • 组合对象内部细节对使用者隐藏
  • 耦合度低,易于维护和测试
  • 可在运行时替换组件

5.3 设计原则:优先使用组合

“类之间的关系既适合继承 (is-a) 也适合组合 (has-a),就用组合。”

原则一:is-a 关系用继承
// ✅ 正确的继承使用:明确的 is-a 关系
class Animal {
public:
    virtual void speak() = 0;
    virtual ~Animal() = default;
};

class Dog : public Animal {  // Dog is an Animal
public:
    void speak() override {
        cout << "Woof!" << endl;
    }
};

class Cat : public Animal {  // Cat is an Animal
public:
    void speak() override {
        cout << "Meow!" << endl;
    }
};
原则二:has-a 关系用组合
// ✅ 正确的组合使用:明确的 has-a 关系
class Engine {
public:
    void start() { cout << "Engine starting..." << endl; }
};

class Car {
public:
    void drive() {
        _engine.start();
    }
    
private:
    Engine _engine;  // Car has an Engine
};
原则三:两者都适合时,优先组合
// ❌ 不推荐:用继承实现 has-a
class MyVector : public vector<int> {  // MyVector is a vector?
public:
    void add(int n) { push_back(n); }
};

// ✅ 推荐:用组合实现 has-a
class MyVector {
public:
    void add(int n) {
        _data.push_back(n);
    }
    
private:
    vector<int> _data;  // MyVector has a vector
};

5.4 组合的优势详解

优势一:低耦合
// 组合:松耦合
class Database {
public:
    void connect() { /* 连接数据库 */ }
    void query() { /* 执行查询 */ }
};

class UserService {
public:
    void setUser(db::Database& db) {
        _db = &db;  // 运行时注入,可替换
    }
    
    void createUser() {
        _db->connect();
        _db->query();
    }
    
private:
    Database* _db;  // 组合,非继承
};
优势二:易于测试
// 组合便于单元测试
class MockDatabase : public Database {
public:
    void connect() override { /* 模拟连接 */ }
    void query() override { /* 模拟查询 */ }
};

void testUserService() {
    UserService service;
    service.setUser(MockDatabase());  // 注入模拟对象
    service.createUser();             // 测试业务逻辑
}
优势三:运行时灵活性
// 组合支持运行时替换
class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    void draw() override { cout << "Drawing Circle" << endl; }
};

class Rectangle : public Shape {
public:
    void draw() override { cout << "Drawing Rectangle" << endl; }
};

class Canvas {
public:
    void setShape(Shape* s) {
        _shape = s;  // 运行时可替换
    }
    
    void render() {
        if (_shape) _shape->draw();
    }
    
private:
    Shape* _shape = nullptr;
};

5.5 继承的适用场景

虽然优先使用组合,但继承在以下场景不可或缺:

场景一:多态实现
// ✅ 继承是实现多态的必要手段
class Logger {
public:
    virtual void log(const string& msg) = 0;
    virtual ~Logger() = default;
};

class FileLogger : public Logger {
public:
    void log(const string& msg) override {
        // 写入文件
    }
};

class ConsoleLogger : public Logger {
public:
    void log(const string& msg) override {
        cout << msg << endl;
    }
};

void process(Logger& logger) {
    logger.log("Processing...");  // 多态调用
}
场景二:类型层次清晰
// ✅ 清晰的继承层次
class Shape {
public:
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double _radius;
public:
    double area() const override { return 3.14159 * _radius * _radius; }
    double perimeter() const override { return 2 * 3.14159 * _radius; }
};

class Rectangle : public Shape {
    double _width, _height;
public:
    double area() const override { return _width * _height; }
    double perimeter() const override { return 2 * (_width + _height); }
};
场景三:代码复用需求明确
// ✅ 当子类确实需要复用父类大量实现时
class FileSystem {
protected:
    string _path;
    
    void open() { /* 打开文件 */ }
    void close() { /* 关闭文件 */ }
};

class FileReader : public FileSystem {
public:
    string read() {
        open();
        // 读取逻辑
        close();
    }
};

class FileWriter : public FileSystem {
public:
    void write(const string& data) {
        open();
        // 写入逻辑
        close();
    }
};

5.6 设计决策流程图

                    ┌──────────────┐
                    │  设计新类     │
                    └──────┬───────┘
                           │
                ┌──────────┴──────────┐
                │                     │
           是否有 is-a            是否有 has-a
             关系?                 关系?
                │                     │
          ┌─────┴─────┐         ┌─────┴─────┐
          │  是        │         │  是        │
          └─────┬─────┘         └─────┬─────┘
                │                     │
        ┌───────┴───────┐     ┌───────┴───────┐
        │               │     │               │
   需要多态?     只是复用实现?  需要运行时替换?
        │               │     │               │
    ┌───┴───┐      ┌────┴───┐ ┌────┴───┐
    │ 是     │      │ 否      │ │ 是      │
    └───┬───┘      └────┬───┘ └────┬───┘
        │               │          │
   使用继承      用组合还是继承?   必须用组合
        │               │
   ┌────┴────┐      ┌────┴────┐
   │ 高耦合   │      │ 低耦合   │
   │ 难测试   │      │ 易测试   │
   └─────────┘      └─────────┘

六、常见错误与避坑指南

6.1 错误一:滥用继承

// ❌ 错误:不合理的使用继承
class Widget : public Button, public Label, public TextBox {
    // 一个 Widget 同时是 Button、Label 和 TextBox?
    // 这是典型的继承滥用
};

// ✅ 正确:使用组合
class Widget {
    Button _button;
    Label _label;
    TextBox _textBox;
    
public:
    void render() {
        _button.render();
        _label.render();
        _textBox.render();
    }
};

6.2 错误二:忽略菱形继承的二义性

// ❌ 错误:忘记处理菱形继承
class Person {
protected:
    string _name;
};

class Student : public Person {};
class Teacher : public Person {};

class Assistant : public Student, public Teacher {
public:
    void showName() {
        // cout << _name << endl;  // ❌ 二义性错误
    }
};

// ✅ 正确:使用虚继承
class Student : virtual public Person {};
class Teacher : virtual public Person {};

6.3 错误三:误解友元继承

// ❌ 错误认知:以为友元可以访问派生类成员
class Base {
    friend void friendFunc();
private:
    int _baseOnly;
};

class Derived : public Base {
private:
    int _derivedOnly;
};

void friendFunc() {
    Derived d;
    d._derivedOnly = 10;  // ❌ 错误!友元不继承
}

// ✅ 正确:友元只授权给声明的类
void friendFunc() {
    Base b;
    b._baseOnly = 10;  // ✅ 正确
    
    Derived d;
    d._derivedOnly = 10;  // ❌ 需要重新声明友元
}

6.4 错误四:忘记虚析构

// ❌ 危险:基类析构函数不是虚函数
class Base {
public:
    ~Base() { cout << "Base 析构" << endl; }
};

class Derived : public Base {
public:
    ~Derived() { 
        cout << "Derived 析构" << endl;
        delete[] _data;  // 可能不会执行!
    }
    
private:
    int* _data;
};

void test() {
    Base* p = new Derived();
    delete p;  // 只调用 Base 析构,Derived 析构不执行!内存泄漏!
}

// ✅ 正确:基类析构函数声明为虚函数
class Base {
public:
    virtual ~Base() { cout << "Base 析构" << endl; }
};

七、总结与记忆口诀

7.1 知识点速查表

知识点一句话总结
禁止继承使用 final 关键字(推荐)或私有构造函数
友元继承友元关系不能继承,基类友元无法访问派生类私有成员
静态成员继承体系中只有一个静态成员实例,所有类共享
菱形继承多继承导致基类成员重复和二义性
虚继承使用 virtual 关键字解决菱形继承问题
继承原则is-a 关系用继承,has-a 关系用组合
设计优先优先使用组合,组合耦合度低、易测试

7.2 记忆口诀

🧠 "继承看 is-a,组合看 has-a;
友元不继承,静态共享它;
多重有菱形,虚继承来救;
想禁止继承,final 最潇洒;
优先选组合,继承留多态。"

7.3 面试高频问题

Q1:如何实现一个不能被继承的类?

A: 方法一:将构造函数设为私有;方法二:使用 C++11 的 final 关键字(推荐)。

Q2:友元关系可以继承吗?

A: 不可以。友元是特殊的授权关系,仅限于声明它的类,不能传递给派生类。

Q3:静态成员在继承体系中如何工作?

A: 整个继承体系共享同一个静态成员实例,无论派生多少个子类。

Q4:什么是菱形继承?如何解决?

A: 菱形继承是多继承的特殊情况,导致基类成员重复和二义性。使用虚继承(virtual)解决,确保公共基类只有一份实例。

Q5:继承和组合有什么区别?如何选择?

A: 继承是 is-a 关系,高耦合;组合是 has-a 关系,低耦合。优先使用组合,只有在需要多态或明确的类型层次时才用继承。

Q6:为什么基类析构函数要声明为虚函数?

A: 确保通过基类指针删除派生类对象时,能正确调用派生类的析构函数,避免内存泄漏。


八、扩展阅读建议

基于本文内容,建议继续深入学习:

  1. 虚函数与多态深入 — 虚函数表机制、纯虚类、抽象类
  2. RTTI 与 dynamic_cast — 运行时类型识别的底层原理
  3. 设计模式 — 策略模式、工厂模式中的继承与组合应用
  4. C++11 新特性overridefinal 关键字的更多用法
  5. 代码重构实践 — 如何将过度继承的代码重构为组合

任何的批评与建议欢迎指出,我们共同进步!
本文原创,欢迎分享,转载请标明出处。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值