C++ ——继承

news/2025/2/23 4:14:49

体现的是代码复用的思想

1、子类继承父类,子类就拥有了父类的特性(成员方法和成员属性)

2、已存在的类被称为“基类”或者“父类”或者“超类”;新创建的类被称为“派生类”或者“子类”

注意:

        (1)子类继承了父类,子类就拥有了父类的属性和方法

        (2)如果子类重写了父类的同名方法,子类对象默认调用的是子类自己的成员方法,如果想调用父类的同名方法,要添加父类作用域限定符

#include <iostream>
using namespace std;
class Father{
public:
    int num1=88;
    int num2=99;
public:
    //成员方法
    void show()const{
        cout<<"num1="<<num1<<",num2="<<num2<<endl;
    }
    void cook(){
        cout<<"做水煮鱼"<<endl;
    }
};
class Son:public Father{  //public:放在继承的地方,表示继承方式是公有继承
public:
    void sing(){
        cout<<"唱歌"<<endl;
    }
    void cook(){
        cout<<"做红烧肉"<<endl;
    }
};
int main(){
    Son s1;
    s1.cook();  //默认子类调用的是自己的成员方法
    s1.show();
    s1.sing();
    s1.Father::cook();  //子类可以使用添加父类作用域限定符,来调用父类的同名方法
    return 0;
}

1、构造函数

1.1 派生类的构造函数

继承中的无参构造函数

(1)基类(父类)中的构造函数不能被继承

(2)派生类(子类)中必须要调用基类中的构造函数来完成属性的初始化

(3)默认情况下,编译器会自动在派生类中调用父类的无参构造函数

(4)子类继承了父类,子类独有的属性需要在自己的构造函数进行初始化,而子类从父类继承过来的属性,必须通过子类构造函数调用父类构造函数进行初始化

#include <iostream>
using namespace std;
class Father{
public:
    int num1=88;
    int num2=99;
public:
    //编译器默认的无参构造函数
    Father(){}
    //有参构造函数
    Father(int num1,int num2)
        :num1(num1),num2(num2){}
};
class Son:public Father{  //public:放在继承的地方,表示继承方式是公有继承
public:
    int a;
public:
    //编译器默认的子类无参调用父类无参构造函数
    Son():Father(){}
    //子类有参构造函数调用父类有参构造函数,来完成继承下来的属性的初始化
    Son(int num1,int num2,int a):Father(num1,num2),a(a){}
};
int main(){
    Son s2(10,55,66);
//    s2.show();
    cout<<s2.a<<endl;  //66
    return 0;
}

1.2 派生类调用基类构造函数

(1)目的:因为构造函数和析构函数,不能继承,所以派生类的构造函数通过调用基类的构造函数完成部分属性的初始化

(2)派生类的构造函数调用基类的构造函数的方式:透传构造、委托构造、继承构造

1.2.1 透传构造

派生类的构造函数直接调用基类的构造函数

class Father{
public:
    int num;
public:
    //无参构造函数---->委托构造
    Father():Father(90){}
    //有参构造函数
    Father(int num)
        :num(num){}
};
class Son:public Father{  //public:放在继承的地方,表示继承方式是公有继承
public:
    int num1;
public:
    //委托构造
    Son():Son(2,88){}
    //透传构造
    Son(int num1,int num):Father(num),num1(num1){}
};

1.2.2 委托构造

可维护性好,但效率相对较低

思想:一个类中的构造函数是可以调用这个类中的其他构造函数

父类的无参构造函数调用父类的有参构造函数;子类的无参构造函数调用子类的有参构造函数;子类的有参构造函数透传父类的有参构造函数

注意:委托构造离不开透传构造

class Father{
public:
    int num;
public:
    //无参构造函数---->委托构造
    Father():Father(90){}
    //有参构造函数
    Father(int num)
        :num(num){}
};
class Son:public Father{  //public:放在继承的地方,表示继承方式是公有继承
public:
    int num1;
public:
    //委托构造
    Son():Son(2,88){}
    //透传构造
    Son(int num1,int num):Father(num),num1(num1){}
};

1.2.3 继承构造

透传构造的简写方式,一句话自动实现透传构造,编译器会自动给派生类添加n个构造函数(n取决于基类中构造函数的个数),并实现自动透传其基类构造函数

class Father{
public:
    int num;
    int age;
    string addr;
public:
    //无参构造函数---->委托构造
    Father():Father(90){}
    //有参构造函数
    Father(int num)
        :num(num){}
    Father(int age,string addr)
        :age(age),addr(addr){}
};
class Son:public Father{  //public:放在继承的地方,表示继承方式是公有继承
public:
    int num1=99;
public:
    //继承构造
    using Father::Father;
};

2、对象的构建和销毁的过程

(1)成员对象:类中有一个成员,该成员是对象类型,叫做对象成员

                先调用成员对象的构造函数,再调用自己的构造函数;析构函数则相反

(2)父子类继承时

                先调用父类的构造函数,再调用子类的构造函数;析构函数则相反

(3)静态成员对象:类中有一个静态成员,该成员是一个对象类型

                静态成员对象的构造函数先执行,再执行自己的构造函数;析构函数则相反

顺序:

        构造函数:静态成员对象---->成员对象---->父类---->子类

        析构函数:子类---->父类---->成员对象---->静态成员对象

2.1 成员对象

class Demon{
public:
    //构造函数
    Demon(){
        cout<<"成员对象的构造函数"<<endl;
    }
    ~Demon(){
        cout<<"成员对象的析构函数"<<endl;
    }
};
class Test{
public:
    Test(){
        cout<<"构造函数"<<endl;
    }
    ~Test(){
        cout<<"析构函数"<<endl;
    }
};

2.2 父子类继承时

class Father{
public:
    Father(){
        cout<<"父类构造"<<endl;
    }
    ~Father(){
        cout<<"父类析构"<<endl;
    }
};
class Son:public Father{
public:
    Son():Father(){
        cout<<"子类构造"<<endl;
    }
    ~Son(){
        cout<<"子类析构"<<endl;
    }
};

2.3 静态成员对象

class P{
public:
    P(){
        cout<<"静态成员对象的构造"<<endl;
    }
    ~P(){
        cout<<"静态成员对象的析构"<<endl;
    }
};
class Phone{
public:
    //类内声明
    static P p1;  //静态成员变量的声明
    Phone(){
        cout<<"构造函数"<<endl;
    }
    ~Phone(){
        cout<<"析构函数"<<endl;
    }
};
P Phone::p1=P();  //类外初始化

3、权限 

3.1 权限修饰符

C++中成员的权限修饰符有3种:

        (1)public:共有的        类内、子类内、类外可以访问

        (2)private:私有的        只能类内访问

        (3)protected:受保护的        类内、子类内可以访问

注意:如果权限修饰符可以省略,默认是private

#include <iostream>
using namespace std;
class Father{
public:
    int num1;
private:
    int num2;
protected:
    int num3;
public:
    //构造函数
    Father(int num1,int num2,int num3)
        :num1(num1),num2(num2),num3(num3){}
    void show()const{
        cout<<this->num1<<" ";  //public 类内可以访问
        cout<<this->num2<<" ";  //private 类内可以访问
        cout<<this->num3<<" ";  //protected  类内可以访问
        cout<<"*******"<<endl;
    }
};
class Son:public Father{
public:
    using Father::Father;
    void fun()const{
        cout<<this->num1<<" "<<this->Father::num1<<" ";  //public 子类内可以访问
        cout<<this->num3<<" "<<this->Father::num3<<" ";  //protected  子类内可以访问
        cout<<"*******"<<endl;
    }
};
int main(){
    Father f1(1,2,3);
    f1.show();
    Son s1(4,5,6);
    s1.fun();
    cout<<s1.num1<<endl;  //public 类外可以访问
    return 0;
}

3.2 继承方式

3.2.1 public(共有继承)

(1)父类是public---->子类也是public

(2)父类是protected---->子类也是protected

(3)父类是private---->子类也是private,但是类内不可以直接访问,可以间接访问

#include <iostream>
using namespace std;
class Father{
public:
    int num1;
protected:
    int num2;
private:
    int num3;
public:
    Father(int num1,int num2,int num3)
        :num1(num1),num2(num2),num3(num3){}
    void fun()const{}
    int get_num3()const{
        return num3;
    }
};
class Son:public Father{
public:
    Son(int num1,int num2,int num3)
        :Father(num1,num2,num3){}
    void show(){
        cout<<this->num1<<endl;  //父类public---->子类也是public,类内可以访问
        cout<<this->num2<<endl;  //父类protected---->子类也是protected
//        cout<<this->num3<<endl;  //报错,父类private---->子类也是private,但是类内不可以直接访问
        cout<<this->Father::get_num3()<<endl;  //父类private---->子类也是private,可以间接访问
        cout<<"******"<<endl;
    }
};
class Sunzi:public Son{
public:
    Sunzi(int num1,int num2,int num3)
        :Son(num1,num2,num3){}
    void fun()const{
        cout<<this->num1<<" "<<this->Son::num1<<endl;  //父类public---->子类也是public,派生类内可以访问
        cout<<this->num2<<" "<<this->Son::num2<<endl;  //父类protected---->子类也是protected
        cout<<"******"<<endl;
    }
};
int main(){
    Son s1(1,2,3);
    s1.show();
    cout<<s1.num1<<endl;  //父类public---->子类也是public,类外可以访问
    Sunzi sz(4,5,6);
    sz.fun();
    return 0;
}

3.2.2 protected(受保护的继承)

(1)父类是public---->子类是protected

(2)父类是protected---->子类也是protected

(3)父类是private---->子类也是private,但是类内不可以直接访问,可以间接访问

#include <iostream>
using namespace std;
class Father{
public:
    int num1;
protected:
    int num2;
private:
    int num3;
public:
    Father(int num1,int num2,int num3)
        :num1(num1),num2(num2),num3(num3){}
    void fun()const{}
    int get_num3()const{
        return num3;
    }
};
class Son:protected Father{
public:
    Son(int num1,int num2,int num3)
        :Father(num1,num2,num3){}
    void show(){
        cout<<this->num1<<endl;  //父类public---->子类是protected
        cout<<this->num2<<endl;  //父类protected---->子类也是protected
//        cout<<this->num3<<endl;  //报错,父类private---->子类也是private,但是类内不可以直接访问
        cout<<this->Father::get_num3()<<endl;  //父类private---->子类也是private,可以间接访问
        cout<<"******"<<endl;
    }
};
class Sunzi:protected Son{
public:
    Sunzi(int num1,int num2,int num3)
        :Son(num1,num2,num3){}
    void fun()const{
        cout<<this->num1<<" "<<this->Son::num1<<endl;  //父类public---->子类是protected
        cout<<this->num2<<" "<<this->Son::num2<<endl;  //父类protected---->子类也是protected
        cout<<"******"<<endl;
    }
};
int main(){
    Son s1(1,2,3);
    s1.show();
    Sunzi sz(4,5,6);
    sz.fun();
    return 0;
}

3.2.3 private(私有继承)

(1)父类是public---->子类是private,类内可以直接访问

(2)父类是protected---->子类是private,类内可以直接访问

(3)父类是private---->子类也是private,但是类内不可以直接访问,可以间接访问

4、多重继承

4.1 概念

继承中,允许有多个基类,每一个基类的继承都可以看作是唯一的独一继承,此时派生类就拥有了所有基类的特性

#include <iostream>
using namespace std;
class Sofa{
public:
    int num=1;
public:
    void job(){
        cout<<"沙发可以坐"<<endl;
    }
};
class Bed{
public:
    void work(){
        cout<<"床可以躺"<<endl;
    }
};
class SofsBed:public Sofa,public Bed{
public:
    void work(){
        cout<<"即可以躺又可以坐"<<endl;
    }
};
int main(){
    SofsBed sfb;
    sfb.job();
    sfb.work();
    cout<<sfb.num<<endl;
    sfb.Bed::work();
    return 0;
}

问题:多个基类同时拥有同名成员函数,此时派生类对象直接调用该函数会产生歧义

解决方法:添加基类作用域限定符

4.2 菱形继承

多继承中的若干个基类,又同时拥有同一个基类,此时叫菱形继承(钻石继承)

#include <iostream>
using namespace std;
//爷爷类
class Furniture{
public:
    void show(){
        cout<<"我们是家具"<<endl;
    }
};
//基类
class Sofa:virtual public Furniture{};
class Bed:virtual public Furniture{};
//派生类
class SofsBed:public Sofa,public Bed{};
int main(){
    SofsBed sfb;
//    sfb.show();  //报错,问题:两个基类拥有了同名函数,产生了歧义
    //解决方法1:使用基类作用域限定符
    sfb.Bed::show();
    sfb.Sofa::show();
    //解决方法2:使用虚继承
    /*继承方式有两种:普通继承(默认的)和虚继承(继承方式前添加virtual关键字)*/
    sfb.show();
    return 0;
}

http://www.niftyadmin.cn/n/5862955.html

相关文章

数据结构之【顺序表简介】

1.顺序表的概念 顺序表 是 用一段物理地址连续的存储单元 依次 存储数据元素的线性结构 一般情况下采用数组存储 2.顺序表的结构 既然顺序表可以用来存储数据元素&#xff0c; 那就少不了 增删查改 的操作 此时&#xff0c;单一地只创建数组满足不了上述操作 创建相应的结构…

【git-hub项目:YOLOs-CPP】本地实现05:项目移植

ok&#xff0c;经过前3个博客&#xff0c;我们实现了项目的跑通。 但是&#xff0c;通常情况下&#xff0c;我们的项目都是需要在其他电脑上也跑通&#xff0c;才对。 然而&#xff0c;经过测试&#xff0c;目前出现了2 个bug。 项目一键下载【⬇️⬇️⬇️】&#xff1a; 精…

使用Odoo Shell卸载模块

使用Odoo Shell卸载模块 我们在Odoo使用过程中&#xff0c;因为模块安装错误或者前端错误等导致odoo 无法通过界面登录&#xff0c;这时候你可以使用Odoo Shell来卸载模块。Odoo Shell是一个交互式Python shell&#xff0c;允许你直接与Odoo数据库和模型进行交互。以下是使用O…

ubuntu22.04离线安装K8S

1. 准备离线安装包 参考教程离线包准备教程 2. 准备环境 2.1. 准备主机 主机名ip系统k8s-master192.168.38.128ubuntu22.04k8s-node192.168.38.131ubuntu22.04 2.2. 设置host 修改 /etc/hosts 文件&#xff0c;添加master和node节点&#xff0c;需要和主机名保持一致 2…

解放大脑!用DeepSeek自动生成PPT!

DeepSeek应用&#xff08;PPT篇&#xff09; DeepSeek作为当前最好的AI大模型之一&#xff0c;其强大的文本生成能力被广泛的应用于各个领域&#xff0c;本文我们来聊聊用DeepSeek来自动生成PPT。 一、DeepSeek & PPT DeepSeek本身没有直接生成PPT的能力&#xff0c;换个…

基于AVue的二次封装:快速构建后台管理系统的CRUD方案

基于AVue的二次封装&#xff1a;快速构建后台管理系统的CRUD方案 在开发后台管理系统时&#xff0c;表格是常见的组件之一。然而&#xff0c;使用原生的Element Plus实现CRUD&#xff08;增删改查&#xff09;功能往往需要编写大量重复代码&#xff0c;过程繁琐。即使借助类似…

Spring AI + Ollama 实现调用DeepSeek-R1模型API

一、前言 随着人工智能技术的飞速发展&#xff0c;大语言模型&#xff08;LLM&#xff09;在各个领域的应用越来越广泛。DeepSeek 作为一款备受瞩目的国产大语言模型&#xff0c;凭借其强大的自然语言处理能力和丰富的知识储备&#xff0c;迅速成为业界关注的焦点。无论是文本生…

大数据SQL调优专题——引擎优化

引入 通过前面对目前大数据领域常见数据处理技术执行原理的梳理&#xff0c;我们会发现一条SQL语句&#xff0c;想要最终转换成集群可执行的物理计划&#xff0c;通常会经过如下图所示的一系列过程&#xff1a; 通常这些数据处理技术都会抽象一个执行器&#xff08;Driver&…