C++ 继承

发布时间:2024年01月04日

为什么需要继承

一般用层次分类的方法来描述他们的关系
在这里插入图片描述

继承的定义

  • 继承是面向对象程序设计使代码可以复用的重要的手段,允许程序员在保持原有的特性基础上进行扩展,增加新功能。
  • 一个B类继承于A类,或者说 从类A派生类B  A是基类(父类),B是派生类(子类)

派生类成员包含两大部分

  • 从父类继承来的 自身固有的
  • 继承来的表现为共性,新增的成员体现个性
class 派生类名字:继承方式 基类名{
 // 派生类新增的数据成员和成员函数
}

/*
继承方式:
public
protected
private
*/

实例

class Person{
public:
    void getName(){
        std::cout<<"name:"<<_name<<std::endl;
    }
    void setName(std::string name){
        _name=name;
    }
    void getAge(){
        std::cout<<"age:"<<_age<<std::endl;
    }
    void setAge(int age){
        _age=age;
    }
protected:
    std::string _name;
    int _age;
};

class Student:public Person{
    /*
     * 继承
     * 相当于student类里面也有
     *     std::string _name;
     *     int _age;
     * */
private:
    std::string _stuId;
};
class Teacher:public Person{
private:
    std::string _teaId;
};


    Student s;
    Teacher t;

    std::cout<<sizeof(Student)<<std::endl;  //72B
    std::cout<< sizeof(Person)<<std::endl;  //40B

继承方式

c++的继承方式会影响子类的对外访问属性

public继承:父类成员在子类保持原有的访问等级
protected继承:父类中public成员变为protected(其他不变)protected依为protected,private依为private
private继承:父类成员在子类中变成private成员

速记技巧:
认为安全等级public<protected<private
继承时,比继承方式等级低(小于)的变为继承方式等级,比继承方式等级高(大于等于)的不变

protected和private继承区别

  • private成员在子类中存在但不可访问,无论何种继承方式,派生类中都不可以访问基类的private类型的成员
  • protected成员在子类中存在可以访问,但子类的对象中不可直接访问
class A{
public:
    int x;
protected:
    int y;
private:
    int z;
};

class B:public A{
public:
    void fun(){
        x;  //和继承方式无关
        y;//public、protected修饰的成员,在派生类可以发访问
        // 报错
        z;//private修饰的成员,在派生类不可以访问
    }
};

单继承构造、析构函数调用顺序

构造函数

  • 子类对象在创建时会首先调用父类的构造函数
  • 父类构造函数执行完毕后,才会调用子类的构造函数
  • 当父类有带参数构造函数时,需要子类初始化列表中显示调用父类构造函数

先有父类后有子类

析构函数

先销毁子类后销毁父类 和构造相反

class A{
public:
    int a;
    A(){
        std::cout<<"A()"<< std::endl;
    }
    A(int a){
        std::cout<<"A(int a)"<< std::endl;
        this->a=a;
    }
    ~A(){
        std::cout<<"~A()"<< std::endl;
    }
};

class B:public A{
public:
    B(){
        std::cout<<"B()"<< std::endl;
    }
	//在派生类中手动调用基类的构造函数
    B(int b):A(b){
        std::cout<<"B(int b):A(b)"<< std::endl;
    }
    ~B(){
        std::cout<<"~B()"<< std::endl;
    }
};

int main() {
    B b;
    /*
	A()
	B()
	~B()
	~A()
	*/

    B a(50);
    /*
	A(int a)
	B(int b):A(b)
	~B()
	~A()
	*/

派生类和基类成员属性冲突

class A{
public:
    int a;
    A(){
        a = 10;
        std::cout<<"A()"<< std::endl;
    }
    ~A(){
        std::cout<<"~A()"<< std::endl;
    }
};

class B:public A{
public:
    int a;
    B(){
        a=20;
        std::cout<<"B()"<< std::endl;
    }

    ~B(){
        std::cout<<"~B()"<< std::endl;
    }
};

	B b;
    std::cout<<sizeof b<<std::endl;//8B,说明派生类可以继承基类同名属性

    // 那么访问b.a是访问的派生类的成员变量还是基类的成员变量呢?
    std::cout<<b.a<<std::endl;//20,同名的时候访问派生类的成员属性

    //怎么访问基类中与派生类中成员同名的属性?
    std::cout<<b.A::a<<std::endl;//10,同名的时候访问基类的成员属性

总结

  • 当子类和父类成员同名时,子类依然从父类继承同名成员
  • 如果子类有成员和父类同名,子类访问其成员默认访问子类的成员(本作用域,就近原则)
  • 在子类通过作用域::进行同名成员区分(在派生类中使用基类的同名成员,显示使用类名限定符)

隐藏

  • 子类函数和父类函数名称相同,参数也相同 而且父类函数没有 virtual 父类函数被隐藏起来
class D{
public:
    void fun(){
        std::cout<<"D"<<std::endl;
    }
};

class C:public D{
public:
    void fun(){
        std::cout<<"C"<<std::endl;
    }
};

    C c;
    c.fun(); //访问的是C的成员函数
    c.D::fun();//访问的是D的成员函数
  • 子类函数和父类函数名称相同,参数不相同,父类函数也被隐藏起来
class D{
public:
    void fun(){
        std::cout<<"D::fun"<<std::endl;
    }
};

class C:public D{
public:
    void fun(int x){
        std::cout<<"C::fun"<<std::endl;
    }
};


    C c;
    c.fun(); //访问不到D类的fun,这样会报错

多继承

在这里插入图片描述

定义格式

class 类名 : 继承方式1 基类名1,继承方式2 基类名2,...,{

}

class A(){
public:
	int a;
};

class B(){
public:
	int b;
};

class C:public A,public B{
public:
	int c;
}

环形继承

多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示:
在这里插入图片描述

class A{
public:
    int a_x;
};

class B:public A{
public:
    //int a_x;
    int b_x;
};

class C:public A{
public:
    //int a_x;
    int c_x;
};

class D:public B,public C{
public:
    //int a_x;
    //int b_x;
    //int c_x;
    int d_x;
};

    D d;
    std::cout<<sizeof d<<std::endl; //20

    std::cout<<d.a_x<< std::endl; //报错 派生类访问间接基类的成员属性是 不知道是为B还是C的

解决环形继承的问题

1、作用域::变量

但是这样没什么意义,还有有多份属性

    std::cout<<d.B::a_x<<std::endl;
    std::cout<<d.C::a_x<<std::endl;

2、虚继承

class A{
public:
    int a_x;
};

//virtual 虚继承,D类继承B和C中A的属性是共享的,也就是说D中只有一份 a_x
class B:virtual public A{
};

class C:virtual public A{
};

class D:public B,public C{
};


    D d;
    
    // 占用空间不同?后续解释 (virtual搞的鬼)
    std::cout<<sizeof(D)<<std::endl; //24
    std::cout<< sizeof(A)<<std::endl; //4

静态成员的继承

  • 静态成员可以被继承而且还是共享
  • 同名基类被隐藏
class AA{
public:
    static int num;
};
int AA::num=100;

class BB:public AA{

};

    BB::num=50;
    //静态成员不仅被继承而且还是共享
    std::cout<<BB::num<<std::endl;//50
    std::cout<<AA::num<<std::endl;//50
class AA{
public:
    static int num;
};
int AA::num=100;

class BB:public AA{
public:
    static int num;
};
int BB::num=100;

    BB::num=50;
    //派生类隐藏基类静态成员变量
    std::cout<<BB::num<<std::endl;//50
    std::cout<<AA::num<<std::endl;//100
文章来源:https://blog.csdn.net/bossDDYY/article/details/135376506
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。