C++继承

本文深入讲解面向对象编程中的继承概念,包括继承的三种关系、继承与转换、派生类的默认成员函数等内容,并探讨了单继承、多继承及菱形继承等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继承

继承是面向对象复用的重要手段。通过继承定义一个类,继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西。函数复用是为了调用它,而类复用是继承它。
继承的三种关系:public(共有),protected(保护),private(私有)。
一个简单的继承关系:
这里写图片描述

class Person
{
public:
    Person(const string& name = " ")
        :_name(name)
    {}
    void Display()
    {
        cout << _name << endl;
    }
protected:
    string _name;
};

class Student:public Person
{
protected:
    int _num;
};

这里写图片描述

继承关系下基类成员在派生类的访问关系变化图:
这里写图片描述
1. 基类的私有成员在派生类成员中是不能被访问的,如果一些基类成员不想被基类对象直接访问,但需要在派生类中能访问,就定义成保护成员。可以看出保护成员限定符是因继承才出现的。
2. public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。
3. protected/private继承是一个实现继承,基类的部分成员并未完全成为子类接口的一部分,是has-a原则关系,所以非特殊情况下不会用这两种关系,在绝大部分的场景下用的是public继承。
4. 不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,但是基类的私有成员存在但是在子类中不可见(不能访问)。
5. 使用关键字class时默认的继承方式是public,不过最好显示的写出继承方式。
6. 在实际运用中一般使用都是public继承,极少场景下才会使用protected/private继承。

继承与转换

  1. 子类对象可以赋值给父类对象(切片/切割)。
  2. 父类对象不能赋值给子类对象。
  3. 父类的指针/引用可以指向子类对象。
  4. 子类的指针/引用不能指向父类对象(可以通过强制转换完成)。
  5. 子类对象切片给父类时,不会将虚表指针给父类。
    这里写图片描述
class Person
{
public:
    void Display()
    {
        cout<<_name<<endl;
    }
protected:
    string _name;
};

class Student:public Person
{
public:
    int _num;
};

void Test()
{
    Person p;
    Student s;

    //子类对象可以赋值给父类对象
    p = s;
    //父类对象不能赋值给子类对象
    //s = p;

    //父类指针/引用可以指向子类对象
    Person* p1 = &s;
    Person& r1 = s;

    //子类指针不能指向父类对象(可以通过强转完成)
    Student* p2 = (Student*)&p;
    //Student& r2 = (Student&)p;

    //会越界
    //p2->_num = 10;
}

继承中的作用域:

1.在继承体系中基类和派生类都有独立的作用域。
2.子类和父类中有同名成员,子类成员将屏蔽父类成员的直接访问。又叫隐藏或重定义
因此最好不要定义重名的成员变量或成员函数。
3.如果定义了重名的函数。子类一般先访问子类的,例如:s._num = 10;若想访问父类的也可以,显示的去调。例如:s.person::_num = 20;

派生类的默认成员函数
在继承关系里,在派生类中如果没有显示定义这六个成员函数(构造函数,拷贝构造函数,析构函数,赋值操作符重载,取地址操作符重载,const修饰的取地址操作符重载),编译系统则会默认合成这六个默认成员函数。

class Person
{
public:
    Person(const char* name = " ")
        :_name(name)
    {
        cout << "Person()" << endl;
    }
    Person(const Person& p)
        :_name(p._name)
    {
        cout << "Person(const Person& p)" << endl;
    }

    Person& operator = (const Person& p)
    {
        cout << "Person operator=(const Person& p)" << endl;
        if (this != &p)
        {
            _name = p._name;
        }
        return *this;
    }
    ~Person()
    {
        cout << "~Person" << endl;
    }

protected:
    string _name;
};

class Student :public Person
{
public:
    Student(const char* name, int num)
        :Person(name)
        ,_num(num)
    {
        cout << "Student()" << endl;
    }

    Student(const Student& s)
        :Person(s)
        ,_num(s._num)
    {
        cout << "Student(const Student& s)" << endl;
    }

    Student& operator = (const Student& s)
    {
        cout << "Student operator=(const Student& p)" << endl;
        if (this != &s)
        {
            Person::operator=(s);
            _num = s._num;
        }
        return *this;
    }
    ~Student()
    {
        cout << "~Student" << endl;
    }
protected:
    int _num;
};
void Test()
{
    Student s1("xiaoming", 12);
    Student s2(s1);
    Student s3("lisa", 18);
    s1 = s3;
}

这里写图片描述

  1. 针对子类来说,子类管自己的构造函数。父类初始化父类,子类初始化子类。
  2. 子类和父类的析构函数都变名为destruct,因此构成隐藏。子类优先调自己,再调父类的析构。

单继承和多继承
1. 单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
2. 多继承:一个子类有两个或两个以上直接父类时称这个继承关系为多继承。

菱形继承

class A
{
public:
    int _a;
};

class B : virtual public A
{
public:
    int _b;
};

class C : virtual public A 
{
public:
    int _c;
};

class D : public B,public C
{
public:
    int _d;
};

int main()
{
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    d._d = 5;
    cout << sizeof(D) << endl;
    return  0;
}

这里写图片描述

Assistant的对象中有两份Person成员,因此菱形继承存在二义性和数据冗余的问题。
虚继承(解决数据冗余和二义性)

  1. 虚继承解决了在菱形继承中子类对象包含多份父类对象的数据冗余(浪费空间)的问题。
  2. 在实际应用中我们通常不会定义如此复杂的继承体系。一般情况不用菱形继承的虚继承体系,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。

    虚函数
    虚函数:在类的成员函数前加virtual关键字,则这个成员函数成为虚函数。
    虚函数重写:当在子类定义了一个父类完全相同的虚函数时,则称子类的这个函数重写(覆盖)了父类这个虚函数。

多态

当使用基类的指针或引用调用重写的虚函数时,当指向父类调用的就是父类的虚函数,指向子类调用的就是子类的虚函数。
多态条件:

  • 虚函数的重写
  • 父类的指针或引用

    总结:
    1.派生类重写基类的虚函数实现多态,要求函数名,参数列表,返回值完全相同。(协变除外)
    2.基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
    3.四个默认成员函数,除了析构,都不要定义成虚函数。
    4.基类必须有virtual关键字。

继承体系同名成员函数的关系:

重载:在同一作用域。函数名相同/参数不同。返回值可以不同。
重写/覆盖:不再同一作用域(分别在基类和派生类)。函数名相同/参数相同/返回值相同(协变除外),基类函数必须由virtual关键字,访问修饰符可以不同。
重定义/隐藏:在不同作用域(分别在基类和派生类),函数名相同。在基类和派生类中只要不构成重写就是重定义。

纯虚函数
在成员函数的后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义后,派生类才能实例化出对象。
友元与继承
友元关系不能继承,也就是说基类友元不能访问子类私有成员和保护成员。

内容概要:本文档详细介绍了基于MATLAB实现多目标差分进化(MODE)算法进行无人机三维路径规划的项目实例。项目旨在提升无人机在复杂三维环境中路径规划的精度、实时性、多目标协调处理能力、障碍物避让能力和路径平滑性。通过引入多目标差分进化算法,项目解决了传统路径规划算法在动态环境和多目标优化中的不足,实现了路径长度、飞行安全距离、能耗等多个目标的协调优化。文档涵盖了环境建模、路径编码、多目标优化策略、障碍物检测与避让、路径平滑处理等关键技术模块,并提供了部分MATLAB代码示例。 适合人群:具备一定编程基础,对无人机路径规划和多目标优化算法感兴趣的科研人员、工程师和研究生。 使用场景及目标:①适用于无人机在军事侦察、环境监测、灾害救援、物流运输、城市管理等领域的三维路径规划;②通过多目标差分进化算法,优化路径长度、飞行安全距离、能耗等多目标,提升无人机任务执行效率和安全性;③解决动态环境变化、实时路径调整和复杂障碍物避让等问题。 其他说明:项目采用模块化设计,便于集成不同的优化目标和动态环境因素,支持后续算法升级与功能扩展。通过系统实现和仿真实验验证,项目不仅提升了理论研究的实用价值,还为无人机智能自主飞行提供了技术基础。文档提供了详细的代码示例,有助于读者深入理解和实践该项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值