当前位置: 首页 > article >正文

C++ ——继承

体现的是代码复用的思想

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.kler.cn/a/558100.html

相关文章:

  • Linux C 静态库如何生成并使用
  • Leetcode K个一组翻转链表
  • win10把c盘docker虚拟硬盘映射迁移到别的磁盘
  • MySQL(高级特性篇)11章——数据库的设计规范
  • 装箱和拆箱是什么?(C#)
  • 【hot100】240搜索二维矩阵
  • A097基于SpringBoot实现的社区博客管理系统
  • vue-treeselect显示unknown的问题及解决
  • 安卓好软-----车机端 安卓tv端很好用的应用管家 adb 授权等等功能
  • 嵌入式0xDEADBEEF
  • 酒店旅游API:数据交互的隐形桥梁——以携程API为例
  • Android-Skin-Loader 换肤框架不支持多个activity同时换肤问题处理
  • Ubuntu DeepSeek磁盘空间不够解决办法
  • linux有名管道
  • 鸿蒙开发技术指南:从入门到精通
  • LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34_中等_C++)(二分查找)(一次二分查找+挨个搜索;两次二分查找)
  • 技术解析 | 适用于TeamCity的Unreal Engine支持插件,提升游戏构建效率
  • netty基础知识梳理和总结
  • 数据库面试知识点总结
  • [Android]浏览器下载的apk文件无法识别无法安装问题