c++中的继承

不能继承的类

1.可以把父类的构造函数私有,就可以使父类不被继承。

cpp 复制代码
class NonInheritable {
private:
    // 将构造函数设为私有
    NonInheritable() {
        std::cout << "NonInheritable 构造函数被调用" << std::cout << std::endl;
    }

2.可以把类设为最后类,也可以不被继承。

cpp 复制代码
class FinalClass final {
public:
    FinalClass() {
        std::cout << "FinalClass 构造函数被调用" << std::endl;
    }

    void display() {
        std::cout << "这是一个最终类" << std::endl;
    }
};

类的前置声明

类只会向上查找,如果在父类有需要调用子类的友元函数;

cpp 复制代码
class Student;
class Person{
public:
    friend void Display(const Person&P,const Student&s);
protected:
    string name;
};
class Student:public Person
{
public:
    friend void Display(const Person&P,const Student&s);
protected:
    int _stunum;
}
void Display(const Person&P,const Student&s){
    cout<<p.name<<endl;
    cout<<s._stunum<<endl;
}

注意:友元关系不会继承,所以在子类也要定义友元。

父类定义了一个static静态成员

父类定义了一个static静态成员,则整个继承体系中只有一个这样的成员。无论派送出多少个子类,只有一个static的实例。

cpp 复制代码
#include <iostream>
using namespace std;

// 1. 定义父类
class Base {
public:
    static int m_count; // 声明静态成员
    Base() { m_count++; } // 构造函数中计数+1
};
// 2. 必须在类外定义并初始化静态成员(这是C++规则)
int Base::m_count = 0;
// 3. 定义两个子类
class DerivedA : public Base {
    // DerivedA 继承自 Base,它没有自己的 m_count,使用的是 Base::m_count
};
class DerivedB : public Base {
    // DerivedB 也继承自 Base,使用的也是 Base::m_count
};
int main() {
    cout << "初始状态 count: " << Base::m_count << endl; // 输出: 0
    // --- 通过父类创建对象 ---
    Base b1;
    cout << "创建 Base b1 后:" << endl;
    cout << "Base::m_count: " << Base::m_count << endl;   // 1
    cout << "DerivedA::m_count: " << DerivedA::m_count << endl; // 1 (共享)
    cout << "DerivedB::m_count: " << DerivedB::m_count << endl; // 1 (共享)
    // --- 通过子类A创建对象 ---
    DerivedA a1, a2;
    cout << "\n创建 DerivedA a1, a2 后:" << endl;
    cout << "Base::m_count: " << Base::m_count << endl;   // 3 (b1+a1+a2)
    cout << "DerivedA::m_count: " << DerivedA::m_count << endl; // 3
    // --- 通过子类B创建对象 ---
    DerivedB b2;
    cout << "\n创建 DerivedB b2 后:" << endl;
    cout << "Base::m_count: " << Base::m_count << endl;   // 4
    cout << "DerivedB::m_count: " << DerivedB::m_count << endl; // 4
    // --- 验证只有一个实例 ---
    // 我们通过子类名字直接修改它
    DerivedA::m_count = 100;
    cout << "\n通过 DerivedA::m_count = 100 修改后:" << endl;
    cout << "Base::m_count: " << Base::m_count << endl;   // 变成了 100
    cout << "DerivedB::m_count: " << DerivedB::m_count << endl; // 也变成了 100
    return 0;
}

继承中的作用域

1在继承体系在父类和子类都有独立的作用域。

2 子类和父类有同名变量,子类会屏蔽掉父类对同名变量的访问,叫隐藏。

(可以使用 父类::父类成员 显示访问)

3,如果是同名函数的隐藏,只需要函数名相同就行。

多继承和菱形继承

每个类只有一个直接父亲时叫单继承;有两个及以上的直接父类时叫多继承。

特殊的有菱形继承

cpp 复制代码
   Class A
     /      \
Class B    Class C
     \      /
      Class D
cpp 复制代码
// 1. 顶层父类(基类)
class Person {
public:
    string m_name = "Person";
};

// 2. 两个中间子类
class Student : public Person {
    // 继承了一份 m_name
};

class Teacher : public Person {
    // 继承了另一份 m_name
};

// 3. 菱形底部的类(多继承)
class Assistant : public Student, public Teacher {
    // 此时 Assistant 内部有两份 Person 的数据!
};
    // 错误!编译器报错:对 'm_name' 的访问不明确
    // cout << assistant.m_name << endl; 
  // 必须显式指定作用域才能访问
    assistant.Student::m_name = "小明";
    assistant.Teacher::m_name = "老王";

c++中引入虚继承

要加上 virtual关键字,必须两个都加virtual关键字。

cpp 复制代码
class Person {
public:
    string m_name = "Default";
};

// 关键点:使用 virtual 继承
class Student : virtual public Person {
};

class Teacher : virtual public Person {
};

class Assistant : public Student, public Teacher {
};
 // 现在可以直接访问了,没有二义性
    assistant.m_name = "Super Assistant";
    
    cout << assistant.m_name << endl; // 输出: Super Assistant

    // 无论通过哪条路径访问,指向的都是同一块内存
    assistant.Student::m_name = "Modified via Student";
    cout << assistant.Teacher::m_name << endl; // 输出: Modified via Student

在那个类冗余和二义性,继承时加virtual

cpp 复制代码
      Class A (最顶层基类)
     /            \
Class B(加v)    Class C(加v)
    |              |
Class F            |
     \             |
      Class G (最底层)

虚继承的弊端

cpp 复制代码
#include <iostream>
using namespace std;

class A {
public:
    A(int val) : m_val(val) { cout << "A constructed with " << val << endl; }
    int m_val;
};

class B : virtual public A {
public:
    // 这里的 A(2) 在单独创建 B 对象时会执行
    // 但在 D 对象中,这个调用会被忽略!
    B() : A(2) { cout << "B constructed" << endl; }
};

class C : virtual public A {
public:
    // 同理,这里的 A(3) 在 D 对象中也会被忽略
    C() : A(3) { cout << "C constructed" << endl; }
};

class D : public B, public C {
public:
    // 必须在这里显式调用 A 的构造函数!
    // 如果漏掉 A(1),编译器会尝试调用 A 的默认构造函数(如果存在)
    D() : A(1), B(), C() { cout << "D constructed" << endl; }
};

int main() {
    D d; 
    // 输出顺序证明 A 是由 D 初始化的:
    // A constructed with 1
    // B constructed
    // C constructed
    // D constructed
    
    cout << "d.m_val = " << d.m_val << endl; // 输出 1
    return 0;
}

继承中的指针

cpp 复制代码
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
             先继承1,再继承2。
int main()
{
    Derive d;
    Base1* p1 = &d;
    Base2* p2 = &d;
    Derive* p3 = &d;对着先继承的

    return 0;
}
p1 == p3 != p2

继承和组合

cpp 复制代码
继承
// 父类:动物
class Animal {
public:
    void eat() {
        cout << "动物正在吃东西..." << endl;
    }
};

// 子类:狗 (继承自 Animal)
// 关系:狗 IS-A 动物
class Dog : public Animal {
public:
    void bark() {
        cout << "汪汪叫!" << endl;
    }
};
/////////////////////////////////////
组合
class Engine {
public:
    void start() {
        cout << "引擎启动了 (轰隆隆...)" << endl;
    }
};

// 容器类:汽车
// 关系:汽车 HAS-A 引擎
class Car {
private:
    Engine _engine; // 组合:Car 拥有一个 Engine 对象
    string _brand;

public:
    Car(string brand) : _brand(brand) {}

    void startCar() {
        cout << _brand << " 准备启动..." << endl;
        // 调用成员对象的方法
        _engine.start(); 
        cout << "汽车启动完成!" << endl;
    }
};

继承是白箱复用,组合是黑箱复用。

白箱意味着,了解父类的实现,从代码逻辑去测试;

黑箱,不了解实现,从功能的角度测试;

耦合度:是模块之间"粘连"的紧密程度。它直接决定了代码修改时的风险大小------耦合度越高,牵一发而动全身的风险就越大;耦合度越低,模块就越独立。

高内聚:相关的功能聚在一起;

高内聚,低耦合是是软件工程的黄金法则。

相关推荐
froginwe112 小时前
CSS 图像透明/不透明
开发语言
初心未改HD2 小时前
Go语言Map底层原理与并发安全深度解析
开发语言·golang
Brilliantwxx2 小时前
【算法题】日期类算法题
开发语言·c++·笔记·程序人生·算法
不会编程的懒洋洋2 小时前
C# IDisposable 和 using
开发语言·笔记·机器学习·c#·.net·visual studio·c#基础
Fighting_p2 小时前
【FileShowCom 组件】文件预览:图片预览 el-image,其余文件预览打开新窗口或者下载
开发语言·前端·javascript
XiYang-DING2 小时前
【Java EE】线程池
java·开发语言·java-ee
想唱rap2 小时前
TCP套接字编程
java·linux·网络·c++·tcp/ip·mysql·ubuntu
xyq20242 小时前
PostgreSQL LIMIT 指令详解
开发语言
小短腿的代码世界2 小时前
Qt 2D 绘制系统核心原理深度解析
开发语言·qt