继承_继承java例子 - CSDN
继承 订阅
按照法律或遵照遗嘱接受死者的财产、职务、头衔、地位等。 展开全文
按照法律或遵照遗嘱接受死者的财产、职务、头衔、地位等。
信息
词    目
继承
拼    音
jì chéng
基本解释
依法承受等
词    性
动词
用    法
谓语等
组    词
~权,~人等
继承基本信息
【词目】继承【拼音】jì chéng【基本解释】泛指把前人的作风、文化、知识等接受过来。
收起全文
精华内容
参与话题
  • 继承

    千次阅读 2018-07-08 12:05:55
    1.什么是继承?(在代码中,在原有类的基础上<属性和方法>,构造一个新的类。) 继承需要符合的关系:is-a的关系;父类更通用更抽象,子类更特殊更具体(因为父类相对于子类而言,父类中有的方法,...
    1.什么是继承?(在代码中,在原有类的基础上<属性和方法>,构造一个新的类。)
        继承需要符合的关系:is-a的关系;父类更通用更抽象,子类更特殊更具体(因为父类相对于子类而言,父类中有的方法,子类全都有;而子类有的属性和方法父类未必有,所以子
    类更具体,父类更抽象)。继承背后的思想就是基于已经存在的类来构建新类,当从已存在的类继承时,就重用了它的方法和属性,还可以添加新的方法和属性来定制新类以应对需求
    (新类称之为子类,被扩建的类称之为父类)。在java当中,除了Object类之外,所有的类都是子类,且都有唯一的一个父类。继承在OOP中不可或缺,创建一个类时,总是在继承
    (因为顶层是Object类,所以当你创建一个类时,都在间接或直接的继承了Object父类,即你创建的类永远为子类)
    (1)类之间的关系
        is-a:继承关系
        Has-a:组合体现(如方向盘对象,轮子对象...组合成了汽车这个对象)
        Like-a:实现接口体现(像...关系,具备...能力的关系)
    (2)继承的意义:代码重用,体现不同抽象层次,为多态做铺垫。
    (3)继承的特点:子类继承了父类的成员(即属性和方法),虽然父类中私有的属性和方法也被继承了,但是访问不到。
    (4)继承的优点:代码重用(父类中的属性和方法可为子类使用);父类字段和方法可用于子类;从抽象到具体形成类的继承体系(也就是具有层次结构);可轻松自定义子类(也就是
        一个类可以构建多个子类)
    (5)在java当中,用extends关键字来表示一个类继承了一个类。


    2.super关键字
        (1)super关键字的特点:
            super关键字和this关键字的特点类似,super关键字代表的是父类对象的引用(保存的是父类引用对象的内存地址),this关键字代表的是当前对象的引用(保存的是当前对象
        的内存地址);当子父类的成员出现同名的时候,可以通过super关键字来区分。子类的构造方法中,通过super关键字来调用父类的构造方法,且必须是子类构造方法中的第一条语句。
        当我们产生子类对象的时候,肯定先要去调用父类的构造方法来产生父类的对象,再产生子类对象。
        (2)方法重写(Override,与方法重载没有关系)
            方法重写是指子类可以根据需要对从父类继承来的方法进行改写,是多态机制的前奏。
        (3)方法重写的注意点:
            重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值;重写方法不能使用比被重写方法更严格的访问权限,父类中的私有方法不能被重写。
        (4)final关键字的特点
            final可以修饰变量、方法、类;final修饰的变量是一个常量,一旦赋了值就不能在修改了。常量一般都和static关键字配合使用。final修饰的类代表此类不能被继承,
            final修饰的方法代表此方法不能被重写。被final修饰的对象,是引用不可变,对象的内容是可变的。
    3.Object类中常见的方法
        java中所有的子类都间接或者直接的继承自java.lang.Object类,可以说Object是java中所有类的祖先类即根类。java中任何类都继承了Object类中的方法,主要有:
        toString():返回该对象的字符串表示
        equals():指示其他对象是否与此对象“相等”
        hashCode():返回该对象的哈希码值
        clone():创建并返回此对象的一个副本
        getClass():返回此Object的运行时类
        finalize():是由垃圾回收器在确定某一个对象没有被引用时,删除清理之前,这个对象调用的。
        String toString():类名@哈希码值
            返回该对象的字符串描述性信息,默认输出的格式是:“类名[字段值,字段值...];”只要对象与一个字符串通过“+”连接,系统就会自动调用toString()以获得对象的字符串描述。
            可以根据用户的需要对其进行重写。
        boolean equals():比较两个引用变量是否指向同一个对象
            Object类原始功能是实现判断两个对象是否具有相同的引用,要求判断两个对象状态的相等性;可以根据需要对该方法进行重写。
    public class Person {
    public static void main(String[] args) {
    Student stu1 = new Student("Jack", 20);
    System.out.println(stu1.toString());
    Student stu2 = new Student("Jack", 20);
    System.out.println(stu1.equals(stu2));
    }
    }
    class Student{
    private String name;
    private int age;

    public Student(String name, int age) {
    this.name = name;
    this.age = age;
    }

    @Override
    public String toString() {
    return "姓名:"+ name + "年龄:" + age;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj) {
    return true;
    }

    if (obj == null) {
    return false;


    if (this.getClass() != obj.getClass()) {
    return false;
    }

    Student stu = (Student)obj;
    if (this.age != stu.age) {
    return false;
    }
    if (this.name == null) {
    if (stu.name != null) {
    return false;
    }
    } else if (!this.name.equals(stu.name)) {
    return false;

    return true;
    }
    }
    展开全文
  • Java核心——继承

    千次阅读 多人点赞 2020-05-24 18:24:15
    继承 extends:译为扩展、延伸之意。在java中,继承是在面向对象的基础上提出的概念。面向对象讲究的是将同类型事物抽象出来,然后封装成类。类相当于是一种模板,根据这种模板产生具体的实例对象,而实例对象则...

    继承


    extends:译为扩展、延伸之意。在java中,继承是在面向对象的基础上提出的概念。面向对象讲的是将同类型事物抽象出来,然后封装成类。类相当于是一种模板,根据这种模板产生具体的实例对象,而实例对象则负责完成具体的业务逻辑。在类型的抽象过程中,有很多属性和方法是通用的,即很多类都具有相同的部分。所以我们需要将这些相同的部分抽离出来,作为基类,子类只要继承了基类,则自动拥有了那些公共的属性。这样就可以减少代码重复,使代码更加简洁,逻辑更加清晰。

    java中所有的类都默认继承自Object类,Object类中包含equals和toString()方法。equals默认比较引用是否相等,而toString默认打印了对象的hashcode信息。通常在使用时我们都会重写这两个方法。如果查看源码会发现不止这两个方法,会有很多native方法。native方法查看不到具体的方法实现,native方法通常是其他语言编写的,使其效率更高。

    java默认为单继承,一个子类只能继承一个父类。但可以是多实现,即一个子类可以实现多个接口。在java8中新增了默认方法。使得在接口中也可以有方法实现,使其可以完成多继承的功能。

    public interface Vehicle {
       default void print(){
          System.out.println("我是一辆车!");
       }
    }
    

    子类继承父类后,就已经包含了父类的所有属性和方法。类中使用this关键字来表示对自身的引用,之前也有提到,我们代码编写时定义的类其实只是一个模板,这个模板定义了这个类的所有元信息,通过这些信息我们就可以实例化出对象,而Class信息在类加载过程中,会被存储到内存模型中的方法区中,也是我们使用反射时获取的数据来源。这里的this并不是指我们编写的这个类,而是具体产生的实例对象。我们知道java中new产生的对象都分配在堆上,所以this其实就是指向这个对象实例在堆上的内存地址。我们声明了一个类模板信息,但是可以new出很多个对象,this在不同的对象实例中就会有不同的值,而我们定义类时使用的关键字更像是一个占位符。

    继承了父类后,不仅有this关键字指向自身,也会有super关键字指向父类。刚才分析到this是指向堆内存的一个引用,同理super也是这样的一个引用。由此可以说明,在实例化子类的同时,也实例化了一个父类对象。下面是类初始化的顺序:

    1. 父类静态成员和静态初始化块
    2. 子类静态成员和静态初始化块
    3. 父类实例成员和实例初始化块
    4. 父类构造方法
    5. 子类实例成员和实例初始化块
    6. 子类构造方法

    所以子类拥有了一个父类的引用,所以我们可以通过这个引用拿到父类的所有信息。所以继承虽然名称叫做继承,但其实可以看做是使用了组合的方式,将父类引用作为子类的一个属性,从而实现了复用和扩展。

    如果使用static修饰的方法中,并不存在this和super的引用。因为static修饰的方法属于类的基本信息,把类信息作为模板理解,模板是记录了类的元数据,所有类在实例化的时候都是通过这些元数据进行初始化。所以只要模板中声明了,那么所有的对象实例中自然也就包含了这些函数。并且Class信息存储在内存模型的方法区中,属于线程共享的区域。所以我们可以直接访问方法区拿到static修饰的类信息,没有必要通过引用去堆中找到对应的内存块,然后拿到对应的数据或者方法,因为他们都是一样的。更加没有必要分辨this还是super,因为这些基本信息在类模板中早已经确定了。

    谈到了继承就会有两个概念需要区分:重载与重写。子类得到父类的所有数据后,如果需要对其功能进行加强或者修改,就需要重新编写这个方法。但其方法签名(包括方法名称、参数类型、参数个数,返回值不属于方法签名的一部分)不改变,仅仅是修改方法体。这时候子类中就拥有了两个方法签名完全相同的方法,一个来自父类,一个是自己重新定义的。在使用时,来自父类的方法会隐藏起来,只有子类重新定义的方法才会生效。这里就涉及到了多态的相关原理。所以重写指的是重新编写新方法替换掉继承下来的方法。而重载其实和继承并没有任何关系,只是因为名称相近所以在这里进行区分。有时候同一件事情会有多种解决方案,但是解决这个事情的过程我们却只有一个命名。比如在超市结账,买一件商品叫做结账,买两件商品也叫结账,买不同种类的商品还是叫结账。在程序中的体现就是为了满足各种情况,我们编写了各自不同的处理方法,但是它们的名称相同,但方法签名不同,也就是参数类型,参数个数不同。这些名称相同的方法就构成了重载。因为名称相同所以可能对我们编程人员可能造成困扰,而在计算机眼里,并不是通过名称来区分方法的不同,而是通过方法签名,所以这些名称相同的方法,在处理过程中自动就被识别成了不同的方法,根本不会有任何的阻碍。所以为什么要把不同的方法命名成一样的名字呢?除了更加符合现实中的习惯,感觉没有任何意义。

    展开全文
  • 面向对象——继承

    2020-06-05 12:18:55
    继承——mypro07.py #测试继承语法结构 class Person: def __init__(self,name,age): self.name = name self.__age = age #私有属性 def say_age(self): pass #print('年龄是{0}'.format(self.age)) ...

    102.继承

    在这里插入图片描述
    在这里插入图片描述

    #测试继承语法结构
    
    class Person:
        def __init__(self,name,age):
            self.name = name
            self.__age = age  #私有属性
    
        def say_age(self):
            pass
            #print('年龄是{0}'.format(self.age))
    
    class Student(Person):
        def __init__(self,name,age,score):
            Person.__init__(self,name,age)
            self.score = score
    
    print(Student.mro())  #Student.mro()显示子类的继承结构
    s = Student("马宝林",18,80)
    s.say_age()
    #print(s.age)  #父类里面是私有的,私有方法属性,子类继承了但是不能用
    print(s._Person__age)
    print(dir(s))
    print(s.name)
    
    D:\python\python37\python.exe F:/python/mypro01/mypro07.py
    [<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
    18
    ['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_age', 'score']
    马宝林
    

    103.方法的重写

    在这里插入图片描述

    #测试继承方法的重写
    
    class Person:
        def __init__(self,name,age):
            self.name = name
            self.__age = age  #私有属性
    
        def say_age(self):
            print('我的年龄是{0}'.format(self.__age))
    
        def say_introduction(self):
            print("我的名字是:{}".format(self.name))
    
    class Student(Person):
        def __init__(self,name,age,score):
            Person.__init__(self,name,age)
            self.score = score
    
        def say_introduction(self):
            #重写父类的方法
            print("报告老师,我的名字是:{}".format(self.name))
    
    
    s = Student("马宝林",18,80)
    s.say_age()
    s.say_introduction()
    
    D:\python\python37\python.exe F:/python/mypro01/mypro08.py
    我的年龄是18
    报告老师,我的名字是:马宝林
    

    104.object根类

    在这里插入图片描述

    class A:
        pass
    class B(A):
        pass
    class C(B):
        pass
    print(C.mro())
    print(C.__mro__)
    
    [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
    

    在这里插入图片描述

    #object跟类
    
    class Person:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
        def say_age(self):
            print("{0}的年龄为:{1}。".format(self.name,self.age))
    
    obj = object()  #根类
    print(dir(obj))
    
    s = Person("马宝林",25,"Man")
    print(dir(s))
    
    
    ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age', 'sex']
    
    

    在这里插入图片描述在这里插入图片描述

    105.重写__str__()方法

    在这里插入图片描述

    #重写object的__str__方法
    
    class Person:
        def __init__(self,name):
            self.name = name
    
        def __str__(self):
            return "此类的名字是:{}".format(self.name)
    
    p = Person("sxt")
    print(p)
    

    106.多重继承

    在这里插入图片描述
    在这里插入图片描述

    #多重继承
    class A:
        def aa(self):
            print("aa")
    
    class B:
        def bb(self):
            print("bb")
    
    class C(B,A):
        def cc(self):
            print("cc")
    
    c = C()
    c.cc()
    c.bb()
    c.aa()
    

    108.super获取父类的定义

    在这里插入图片描述

    #测试super(),代表父类的定义,而不是对象
    class A:
        def say(self):
            print("A:",self)
    
    class B(A):
        def say(self):
            #A.say(self)
            super().say()  #与A.say()的效果一样
            print("B:",self)
    
    c = B()
    c.say()
    
    D:\python\python37\python.exe F:/python/mypro01/mypro11.py
    A: <__main__.B object at 0x000001998687D668>
    B: <__main__.B object at 0x000001998687D668>
    

    109.多态

    在这里插入图片描述

    #多态
    #根据对象不同调用不同的方法
    class Man:
        def eat(self):
            print("我饿啦!我要吃饭。")
    class Chinese(Man):
        def eat(self):
            print("中国人用筷子吃饭。")
    class English(Man):
        def eat(self):
            print("英国人用叉子吃饭。")
    class Indian(Man):
        def eat(self):
            print("印度人用右手吃饭。")
    
    def manEat(m):
        if isinstance(m,Man):   #Chinese是Man的子类
            m.eat()
        else:
            print("现在不可以吃饭。")
    
    manEat(Chinese())
    

    110.特殊方法和运算符重载

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    #运算符的重载
    
    class Person:
        def __init__(self,name):
            self.name = name
    
        def __add__(self,other):
            if isinstance(other,Person):
                return "{0}--{1}".format(self.name,other.name)
            else:
                print("不是同一类不可以相加。")
        def __mul__(self, other):
            if isinstance(other,int):
                return self.name*3
    
    
    p1 = Person("mabaolin")
    p2 = Person("maiyulong")
    x = p1+p2
    print(x)
    print(p1*3)
    
    
    D:\python\python37\python.exe F:/python/mypro01/mypro13.py
    mabaolin--maiyulong
    mabaolinmabaolinmabaolin
    

    特殊属性

    在这里插入图片描述

    class A:
        pass
    class B(A):
        pass
    class C(B):
        def __init__(self,nn):
            self.nn = nn
        pass
    c = C(3)
    print(dir(c))  #对象的所有属性和方法
    print(c.__dict__)  #对象的属性字典
    print(c.__class__)  #对象所属的类
    print(C.__bases__)  #类的积累元组(多继承)
    print(C.__mro__)  #类的层次结构
    print(A.__subclasses__())  #子类列表
    
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'nn']
    {'nn': 3}
    <class '__main__.C'>
    (<class '__main__.B'>,)
    (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
    [<class '__main__.B'>]
    
    

    对象的浅拷贝和深拷贝

    在这里插入图片描述

    #浅拷贝和深拷贝
    import copy
    class MobilPhone:
        def __init__(self,cpu,screen):
            self.cpu = cpu
            self.screen = screen
    class CPU:
        def calculate(self):
            print("数字计算。")
            print("cpu对象:",self)
    class Screen:
        def show(self):
            print("显示一幅好看的画。")
            print("screen对象:",self)
    #测试变量赋值
    '''
    
    m1 = MobilPhone()
    m2 = m1
    print(m1)
    print(m2)
    '''
    #测试浅拷贝
    c1 = CPU()
    s1 = Screen()
    m1 = MobilPhone(c1,s1)
    m2 = copy.copy(m1)
    
    print("测试浅拷贝")
    print(m1,m1.cpu,m1.screen)#cpu和screen一样,m1和m2不一样
    print(m2,m2.cpu,m2.screen)
    
    #测试深拷贝
    m3 = copy.deepcopy(m1)
    print("测试深拷贝")
    print(m1,m1.cpu,m1.screen)#cpu和screen都不一样
    print(m3,m3.cpu,m3.screen)
    
    D:\python\python37\python.exe F:/python/mypro01/mypro14.py
    测试浅拷贝
    <__main__.MobilPhone object at 0x000001E153B3DEF0> <__main__.CPU object at 0x000001E1538560F0> <__main__.Screen object at 0x000001E153B3DE10>
    <__main__.MobilPhone object at 0x000001E153B52198> <__main__.CPU object at 0x000001E1538560F0> <__main__.Screen object at 0x000001E153B3DE10>
    测试深拷贝
    <__main__.MobilPhone object at 0x000001E153B3DEF0> <__main__.CPU object at 0x000001E1538560F0> <__main__.Screen object at 0x000001E153B3DE10>
    <__main__.MobilPhone object at 0x000001E153B625F8> <__main__.CPU object at 0x000001E153B625C0> <__main__.Screen object at 0x000001E153B626D8>
    
    展开全文
  • C++| |继承

    2019-01-14 15:31:14
    继承 1.概念 继承(inheritance)是面向对象程序设计使代码复用的手段。它允许程序员在保持原有类特性的基础上进行扩展,从而产生新的类,称为派生类也叫作子类。继承是设计层次的复用   2. 定义 2.1 定义...

    继承


    1.概念

    继承(inheritance)是面向对象程序设计使代码复用的手段。它允许程序员在保持原有类特性的基础上进行扩展,从而产生新的类,称为派生类也叫作子类继承是设计层次的复用

     

    2. 定义

    2.1 定义格式

    插图:定义图

    注意:继承符号是:冒号

     

    2.2 继承方式和访问限定符

    插图:继承方式

    插图:访问限定符

     

    2.3 继承基类成员访问方式的变化

    类成员/继承方式 public继承 protected继承 private继承
    基类的public成员 派生类的public成员 派生类的protected成员 派生类的private成员
    基类的protected成员 派生类的protected成员 p派生类的rotected成员 派生类的private成员
    基类的private成员 在派生类中不可见 在派生类中不可见 在派生类中不可见

    总结:

    1. 基类的private成员在派生类中无论以什么方式继承都是不可见的不可见:基类的私有成员依旧被继承到了派生类中,但是语法上限制了派生类对象不管在类外还是类内都不能去访问它

    2. 如果基类的成员不想在类外被访问,但需要在派生类中能访问,就定义为protected。保护限定符是因继承才出现的

    3. 对于上面表格进行总结。基类的私有成员在子类都是不可见的。基类的其他成员在子类的访问方式=Min(成员在基类的访问限定符,继承方式),public > protected > private

    4. 关键字class默认的继承方式是private,struct默认的继承方式是public

    5. 在实际应用中一般使用的都时public继承,几乎很少使用protected和private继承。因为protected和private继承下来的成员只能在派生类中使用,实际中扩展维护性不高

     

    3. 基类和派生类对象赋值转换

    • 派生类的对象可以赋值给基类的对象,基类的指针,基类的引用。形象的说法是切片

    • 基类对象不可以赋值给派生类对象

    • 基类的指针可以通过强制转换赋值给派生类的指针。但是必须是基类的指针指向派生类的对象时才是安全的。

    例:

    #include <iostream>
    #include <string>
    
    class Person
    {
    public:
    	void Show()
    	{
    		std::cout << "In Basci!" << std::endl;
    	}
    
    	//private:
    	std::string _name;
    	int _age;
    };
    
    class Student : public Person
    {
    public:
    	void Display()
    	{
    		std::cout << "In Derived!" << std::endl;
    	}
    
    	//private:
    	int _num;
    };
    
    int main()
    {
    	//派生类的对象可以赋值给基类的对象,引用,指针
    	Student stu;
    	Person p = stu;
    	Person& p1 = stu;
    	Person* p2 = &stu;
    
    	//基类的对象不可以赋值给派生类
    	//stu = p;
    
    	//基类的指针(指向派生类对象)可以赋值给派生类,必须要进行强制类型转换
    	Student* pStu = (Student*)p2;
    	pStu->_num = 10;
    	std::cout << "ok" << std::endl;
    
    	//基类的指针(指向基类的对象)可以赋值给派生类,但是会发生越界问题
    	Person pp;
    	pStu = (Student*)&pp;
    	pStu->_num = 10;
    	std::cout << "odk?" << std::endl;
    	return 0;
    }

     

     

     

    4. 继承中的作用域

    1. 在继承体系中基类和派生类都有着自己的独立作用域

    2. 子类和父类有同名的成员的话,子类成员将屏蔽父类对于同名成员的直接访问,这种情况叫做隐藏,也叫作重定向。(在子类成员函数中,可以使用基类::基类成员 显式访问)

    3. 实际中在继承体系中最好不要定义同名的成员

    例:

    #include <iostream>
    #include <string>
    
    class Person
    {
    protected:
    	int _num = 111;
    	std::string _name = "yk";
    };
    
    class Student : public Person
    {
    public:
    	void Show()
    	{
    		std::cout << "_num: " << _num << std::endl;
    		std::cout << "_name: " << _name << std::endl;
    	}
    
    private:
    	int _num = 999;
    	std::string _name = "oodk";
    };
    
    int main()
    {
    	Student stu;
    	stu.Show();//输出999 oodk表示基类的成员变量被隐藏了
    	return 0;
    }

    理解重载和隐藏:

    例:

    #include <iostream>
    #include <string>
    
    class A
    {
    public:
    	void Show()
    	{
    		std::cout << "In A!" << std::endl;
    	}
    };
    
    class B : public A
    {
    public:
    	void Show(int i)
    	{
    		std::cout << "In B!" << std::endl;
    	}
    };
    
    int main()
    {
    	//A和B中的Show函数构成了隐藏,因为不在同一个作用域中所以不是重载
    	B b;
    	b.Show(3);
    	b.A::Show();//调用父类中的Show函数必须要显式调用,因为被隐藏了
    	return 0;
    }

     

     

    5. 派生类的默认成员函数

    6个默认的成员函数

    1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函(无参的构造函数),则必须在派生类的构造函数中初始化链表阶段显式调用(可以在初始化阶段的任何一个地方)

    2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化

    3. 派生类的operator=必须要调用基类的operator=完成基类的复制

    4. 派生类的析构函数会在被调用完成之后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类的对象先清理派生类成员在清理基类成员的顺序

    插图:六大默认成员函数

    插图:默认成员函数调用顺序

     

    例:

    #include <iostream>
    #include <string>
    
    
    class Person
    {
    public:
    	Person(std::string name)
    		: _name(name)
    	{
    		std::cout << "In Person Construction!" << std::endl;
    	}
    
    	Person(const Person& p)
    		: _name(p._name)
    	{
    		std::cout << "In Person Construction=!" << std::endl;
    	}
    
    	Person& operator=(const Person& p)
    	{
    		if (this != &p)
    		{
    			_name = p._name;
    			std::cout << "In Person operator=!" << std::endl;
    		}
    
    		return *this;
    	}
    
    	~Person()
    	{
    		std::cout << "In Person Destruction!" << std::endl;
    	}
    
    private:
    	std::string _name;
    };
    
    
    class Student : public Person
    {
    public:
    	Student(std::string name)
    		: Person(name)
    		, _name(name)
    	{
    		std::cout << "In Student Construction!" << std::endl;
    	}
    
    	Student(const Student& p)
    		: Person(p)
    		, _name(p._name)
    	{
    		std::cout << "In Student Construction=!" << std::endl;
    	}
    
    	Student& operator=(const Student& p)
    	{
    		if (this != &p)
    		{
    			//必须先对基类的成员进行拷贝构造
    			Person::operator=(p);
    			_name = p._name;
    			std::cout << "In Student operator=!" << std::endl;
    		}
    
    		return *this;
    	}
    
    	~Student()
    	{
    		std::cout << "In Student Destruction!" << std::endl;
    	}
    
    private:
    	std::string _name;
    };
    
    int main()
    {
    	Student s1("jack");
    	Student s2(s1);
    	Student s3("rose");
    	s1 = s3;
    	return 0;
    }

    【面试题】:实现一个不能被继承的类

    • C++98:构造函数私有化,派生类调用不到基类的构造函数,则无法继承

    • C++11:使用关键字final(最终)

     

    6. 继承与友元

    友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

    例:

    class A
    {
    public:
    	friend void Display(const A& a);
    public:
    	void Show()
    	{
    		std::cout << "In A!" << std::endl;
    	}
    
    private:
    	int _num = 34;
    };
    
    
    class B : public A
    {
    public:
    	void Show()
    	{
    		std::cout << "In B!" << std::endl;
    	}
    
    private:
    	std::string _name = "yk";
    };
    
    void Display(const A& a)
    {
    	std::cout << "Freind function In A!" << "_num" << a._num << std::endl;
    	//std::cout << "Freind function In A!" << "_num" << b._name << std::endl; 
    }
    
    int main()
    {
    	A a;
    	Display(a);//输出Friend function In A!_num34 
    	return 0;
    }

     

     

    7. 继承与静态成员

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

    例:

    class A
    {
    public:
    	friend void Display(const A& a);
    public:
    	void Show()
    	{
    		std::cout << "_count" << _count << std::endl;
    	}
    
    	A()
    	{
    		_count++;
    	}
    
    private:
    	int _num = 34;
    
    public:
    	static int _count;
    };
    
    int A::_count = 0;
    
    
    class B : public A
    {
    public:
    	void Show()
    	{
    		std::cout << "In B!" << std::endl;
    	}
    
    private:
    	std::string _name = "yk";
    };
    
    class C : public A
    {
    
    private:
    	int _c = 0;
    };
    
    void Display(const A& a)
    {
    	std::cout << "Freind function In A!" << "_count" << a._count << std::endl;
    	//std::cout << "Freind function In A!" << "_num" << b._name << std::endl; 
    }
    
    int main()
    {
    	B b1;
    	B b2;
    	B b3;
    	Display(b3);//_count 3
    
    	C c;
    	c.Show();//_count 4
    	return 0;
    }

     

     

    8. 复杂菱形继承和菱形虚拟继承

    基于代码

    class Person
    {
    
    private:
    	std::string _name;
    };
    
    class Student : public Person
    {
    
    private:
    	int _num;
    };
    
    class Teacher : public Person
    {
    
    private:
    	int _id;
    };
    
    class Assistant : public Student, Teacher
    {
    
    private:
    	std::string _majorCourse;
    };

    单继承:一个子类只有一个直接父类

    插图:

    多继承:一个子类有两个或者以上直接父类

    插图:

    菱形继承:菱形继承是多继承的一种特殊情况

    插图:

    菱形继承的问题:菱形继承有数据冗余和二义性问题

    例:

    class Person
    {
    
    public:
    	std::string _name;
    };
    
    class Student : public Person
    {
    
    protected:
    	int _num;
    };
    
    class Teacher : public Person
    {
    
    protected:
    	int _id;
    };
    
    class Assistant : public Student, public Teacher
    {
    
    protected:
    	std::string _majorCourse;
    };
    
    int main()
    {
    	Assistant as;
    	//as._name = "YK";//出现错误,因为数据二义性,必须显式调用
    	as.Student::_name = "YK";
    	as.Teacher::_name = "LW";
    	return 0;
    }

    【解决方法】

    虚拟继承可以解决菱形继承的二义性和数据冗余的问题。在上面的继承关系中,在Student和Teacher继承Person使用虚拟继承,即可解决问题.

    虚拟继承就是让最终继承下来的子类之后只有一个_name数据,也就对不管对于改变Student还是Teacher都是将其全部都改变了。不会在出现改变Student中的 _name,Teacher中的 _name没有发生改变

    例:

    class Person
    {
    
    public:
    	std::string _name;
    };
    
    class Student : virtual public Person
    {
    
    protected:
    	int _num;
    };
    
    class Teacher : virtual public Person
    {
    
    protected:
    	int _id;
    };
    
    class Assistant : public Student, public Teacher
    {
    
    protected:
    	std::string _majorCourse;
    };
    
    int main()
    {
    	Assistant as;
    	as._name = "YK";//可以使用了,由于只有一个_name数据了,不会在产生二义性的问题
    	as.Student::_name = "YK";
    	as.Teacher::_name = "LW";
    	return 0;
    }

    虚拟菱形继承的原理:

    使用以下例子来再借助内存窗口观察对象成员的模型

    • 菱形继承的内部结构

    class A
    {
    
    public:
    	int _a;
    };
    
    class B : public A
    {
    
    public:
    	int _b;
    };
    
    class C : public A
    {
    
    public:
    	int _c;
    };
    
    class D : public B, public C
    {
    
    public:
    	int _d;
    };
    
    int main()
    {
    	D d;
    	d.B::_a = 2;
    	d.C::_a = 1;
    	d._b = 2;
    	d._c = 3;
    	d._d = 4;
    	return 0;;
    }

    这是没有虚拟继承,只是菱形继承,就会产生数据二义性问题。对于菱形继承对于类D创建的对象里面只有两个类,类B和类C,但是类B和类C内部又有一个类类A,这样就会产生对于类D创建一个对象这个两个类A,这样就会产生数据冗余和二义性问题。

    插图:内存模型

     

    可以看到对于一个对象d,内部有着两个类B和C,但是对于B和C内部有分别有着一个类A,这样就有两个类A存于对象d中,就会导致数据冗余和二义性。

    画图理解:

    插图:

     

    可以看到一个对象中有着两个_a数据

     

    • 虚拟菱形继承内部结构

    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._a = 0;//可以赋值,没有二义性问题
    	d.B::_a = 2;
    	d.C::_a = 1;
    	d._b = 2;
    	d._c = 3;
    	d._d = 4;
    	return 0;;
    }

    对于虚拟菱形继承,对于一个对象d来说,内部也是有着类B和类C,类B和类C内部都有着一个指向类A的指针,虚拟菱形继承相对于菱形继承,就是对于类A不再是类B和类C私有的了,类A对于类B和类C是共有的

    如图:内存结构

     

    内部不仅有着类B类C还有这着一个类B和类C共有的类A,类B和类C内部都有一个指向类A的指针

    如何实现不存在数据二义性问题:

    • 但是对于属于类B和类C的类A可以看到它内部没有地址空间,所以其实他只是一个指针,也就是一个地址

    • 只有共有的类A才有这地址空间可以存放数据_a,所以对于类B和类C内部的类A的指针,指向的就是这个独立的类A。从而实现了数据一致性了

    d对象将类A放到了组成的最下面,这个类A同时属于类B和类C,那么B和C如何去找到共有的A呢?

    • 这里是通过B和C的两个指针,指向的一张表。这两个指针叫做虚基表指针,这两个表叫做虚基表。虚基表中存在着偏移量,通过偏移量可以找到下面的类A

    • 如图:

    可以看到对于类B和类C都有着一个虚基表指针,指向一个虚基表,该虚基表下面就是偏移量,从该位置偏移多少就可以找到共有的类A

    对于对象d中的类B和类C为什么要去找属于自己的类A呢?

    • 因为对于类B和类C均是类D的父类。如果是父类的话就有可能产生切片,所以就要找到属于自己的数据,这样的话才不会发生切片错误

    对于Person关系菱形虚拟继承的原理解释:

    如图:

     

     

    10. 总结

    1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有了菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题

    2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如java

    3. 继承和组合

      • public继承是一种is-a的关系,也就是说每个派生类对象都是一个基类对象

      • 组合是一种has-a的关系。假设B组合了A,每个B对象都有着一个A对象

      • 优先使用对象组合,而不是类继承(高内聚,低耦合)

      • 继承允许你根据基类类的实现来定义派生类的实现,这种通过生成派生类的复用称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见。继承一定程度破坏了基类的封装,基类的改变,对于派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。

      • 对象组合是类继承之外的另一种复用选择。对象组合要求被组合的对象具有良好定义的接口。这种复用风格称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于保持每一个类被封装,内聚性高。

      • 实际中尽量使用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地,有些关系就适合用继承,另外实现多态,也必须要继承。类之间的关系可以使用继承,可以使用组合就用组合。

     

    展开全文
  • C++中的继承

    万次阅读 多人点赞 2019-06-02 22:41:24
    1.为什么要使用继承 继承性是一个非常自然的概念,现实世界中的许多事物都是具有继承性的。人们一般用层次分类的方法来描述他们的关系。例如,下图就是一个简单的汽车分类图: 在这个分类树中建立了一个层次结构,...
  • c++的继承详解

    万次阅读 多人点赞 2016-10-15 21:12:51
    继承是c++语言一个重要的机制,该机制自动地为一个类提供来自另一个类的操作和数据结构,这使得程序员只需在新类中定义已有的类中没有的成分来建立一个新类。二、继承解释 继承是类的重要特性。A类继承B类,我称B...
  • C++ 类(继承的基础知识)

    万次阅读 多人点赞 2018-07-29 12:53:54
    继承的基础知识; 派生类的语法定义; 继承的重要说明; 派生类的访问控制; 派生类访问控制的结论; 类之间的关系 1.包含关系: A类中的一个数据成员是B类。 2. User关系: A类部分使用B类。通过类之间的成员函数...
  • js继承的几种方式

    2019-05-05 00:09:55
    js继承的几种方式1、构造函数继承2、原型链继承3、组合继承4、组合继承优化15、组合继承优化26、组合继承优化3 1、构造函数继承 function Parent1(name){ this.name= name || 'Parent1'; } Parent1.prototype....
  • Flask中的继承

    万次阅读 2018-10-30 11:23:25
    首先在被继承的网页index.html中使用{% block blockname %} 可以在继承网页被改被覆盖的内容{% endblock %} ...使用{% block blockname %} 可以覆盖被继承页被圈的内容{% endblock %} 注意 blocknam...
  • 这个继承,不仅仅是一级的继承关系,包括好几层的继承。父类的父类的父类。直到最后。 可以很清楚明了的了解一个类的实现关系。diagram 英[ˈdaɪəgræm] 美[ˈdaɪəˌɡræm] n. 图表; 示意图; 图解; [数] 线图;....
  • Python多继承与super使用详解

    万次阅读 多人点赞 2018-08-17 12:51:38
    Python虽然支持多继承,但是python支持的多继承也是有限的。 0.问题的提出 如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢? Python 中的 MRO —— 方法搜索顺序 Python中...
  • Java 继承

    千次阅读 多人点赞 2019-05-07 11:12:43
    java继承的概念 Java继承是面向对象的最显著的一个特征。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为, 并能扩展新的能力, 继承使用的是extends 类的继承格式 class 父类 { } class ...
  • maven中的继承关系和聚合关系

    万次阅读 2020-01-25 23:31:52
    为什么需要继承关系? 非compile范围的依赖(不参与打包)不能传递依赖关系 例如多个模块中使用了junit依赖,单元测试一般不参与打包所以范围一般是test范围,这样每个模块的junit版本很可能不一致,可能会导致...
  • Python:类的继承,调用父类的属性和方法基础详解

    万次阅读 多人点赞 2018-12-30 11:35:01
    备注:这里省略了关于继承含义的解释,直接上代码了,更直观点 子类:Son类 父类:Father类 以下案例均表示Son类继承父类的一些属性和初始化参数构造等。 5个模块: (1):直接调用父类属性方法; (2):重写...
  • //用C语言模拟实现继承继承继承权限public->protected->private)继承是面向对象复用的重要手段,通过继承定义一个类,继承是关系类型之间的建模, 共享共有的东西,实现各自本质不同的东西。在继承关系当中,派生...
  • 7.1.4继承

    万次阅读 2020-02-03 20:52:03
    继承 生活中的继承 类的继承格式 在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下: class 父类 { } class 子类 extends 父类 { } 为什么需要继承 接下来我们通过...
  • JS实现继承的几种方法总结

    万次阅读 多人点赞 2019-06-12 09:05:15
    首先定义一个父类: //构造函数 function Animal(name) { this.name = name || 'Animal'; this.sleep = function() { console.log(this.name + '正在睡觉!'); };...Animal.prototype.eat = function(food) { ...
  • python类继承实例

    万次阅读 2018-08-27 22:26:33
    class Bicycle: def init(self): pass def run(self,km): print('自行车骑行了',km,'公里') class EBicycle(Bicycle): def init(self,vol=0): self.vol=vol print(‘新买的电动车...
  • Mybatis表对象继承实现

    万次阅读 2017-04-27 10:49:09
    Mybatis表对象继承  我们一般用表来表现对象之间的继承关系时通常有三种方式。第一种是把所有对象包含的属性都存放在一张表中,然后用一个字段来区分当前记录对应的对象类型;第二种是每个子类型一张表,每张表都...
  • Maven的聚合与继承

    千次阅读 2018-11-08 21:39:03
    (尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/83867717冷血之心的博客) 关注微信公众号(文强的技术小屋),学习更多技术知识,一起遨游知识海洋~ ...
1 2 3 4 5 ... 20
收藏数 2,173,749
精华内容 869,499
关键字:

继承