十一、C++继承

本文详细介绍了C++中面向对象编程的核心概念,包括继承机制、派生类定义、访问控制规则、构造函数和析构函数的调用顺序、隐藏问题以及多继承、虚继承和静态成员的继承。
摘要由CSDN通过智能技术生成

一、继承的概念

1、继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保存原有的特性基础上进行扩展,增加功能

2、一个B类继承于A类,或称从类A派生类B。这样类A成为基类(父类),类B成为派生类(子类)。

派生类中的成员,包含两大部分:

· 一类是从基类继承过来的,一类是自己增加的成员

· 从基类继承过来的表现其共性,而新增的成员体现了其个性

二、派生类定义

1、语法

Class 派生类名 : 继承方式 基类名
{
    //派生类新增的数据成员和成员函数
};

三种继承方式:
        public:    公有继承
        private:   私有继承
        protected: 保护继承

 2、例如

class Person    //定义一个Person基类
{
public:
    void Print()
    {
        cout<<"name: "<<_name<<endl;
        cout<<"age: "<<_age<<endl;
    }
    void set_name(string name)
    {
        _name = name;
    }
    string get_name()
    {
        return _name;
    }
    void set_age(int age)
    {
        _age = age;
    }
    int get_age()
    {
        return _age;
    }
protected:
    string _name;   //名字
    int _age;       //年龄
};

//Student 派生类
class Student : public Person
{
private:
    string _stuID;
};

//Teacher 派生类
class Teacher : public Person
{
private:
    string _jobID;
};

int main() {
    Teacher t;
    Student s;
    t.set_age(10);
    t.set_name("zhanshan");
    s.set_age(30);
    s.set_name("Tony");
    t.Print();
    s.Print();
    cout<<sizeof(Person)<<endl;
    cout << sizeof(t) <<endl;
    cout << sizeof(s) <<endl;
    return 0;
}

输出可以看出:派生类与基类所占大小多了新增的数据成员。

三、派生类的访问控制

 1、C++中的继承方式会影响子类的对外访问属性

public:    父类成员在子类中保持原有的访问级别
private:   父类成员在子类中变为private成员
protected: 父类中public成员会变成protected,父类中protected成员仍然为protected,父类中private仍然为private

                    父类成员访问控制

继承方式

publicprotectedprivate
public
publicprotectedprivate
protected
protectedprotectedprivate
private
privateprivateprivate

2、private成员在子类中依然存在,但是却无法访问到。不论何种方式继承基类,派生类都不能直接使用基类的私有成员。 

private 和 protected 的区别

  • private成员是完全私有的,只有类本身可以访问,用于实现类的内部逻辑和数据隐藏。
  • protected成员在类内部和派生类中可见,用于在继承体系中共享代码和实现多态。

四、单继承中构造函数和析构函数的调用顺序

·子类对象在创建时会首先调用父类的构造函数

·父类构造函数执行完成后,才会调用子类的构造函数

·当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数

·析构函数调用顺序和构造函数相反

class Person    //定义一个Person基类
{
public:
    Person()
    {cout<<"Person()"<<endl;}
    ~Person()
    {cout<<"~Person()"<<endl;}
}
//Student 派生类
class Student : public Person
{
public:
    Student()
    {cout<<"Student()"<<endl;}
    ~Student()
    {cout<<"~Student()"<<endl;}
}
int main() {
    Student s;
    return 0;
}

输出:

五、派生类中调用基类的构造函数

 方法一:基类中设计函数访问所有变量,派生类在构造函数内初始化成员变量

class Person    //定义一个Person基类
{
public:
    Person()
    {cout<<"Person()"<<endl;}
    ~Person()
    {cout<<"~Person()"<<endl;}

    //用于访问私有变量
    void set_name(const char* name)
    {_name = name;}
    string get_name()
    {return _name;}
    void set_age(int age)
    {_age = age;}
    int get_age()
    {return _age;}
protected:
    int _age;       //年龄
private:
    string _name;   //名字
};

//Student 派生类
class Student : public Person
{
public:
    Student()
    {cout<<"Student()"<<endl;}
    Student(int age,const char *name)
    {
        _age = age;
        set_name(name);//调用函数初始化私有变量
        cout<<"Student(int age,char *name)"<<endl;
    }
    ~Student()
    {cout<<"~Student()"<<endl;}
private:
    string _stuID;
};

int main() {
    Student b(10,"lisi");
    cout<<b.get_age()<<endl;
    cout<<b.get_name()<<endl;
    return 0;
}

输出: 

方法二: 基类中的构造函数使用初始化成员列表初始化成员变量

在派生类中可以指定调用基类中的构造函数

class Person    //定义一个Person基类
{
public:
    Person()
    {cout<<"Person()"<<endl;}
    ~Person()
    {cout<<"~Person()"<<endl;}
    //基类构造函数使用初始化成员列表
    Person(int age,const char* name):_age(age),_name(name)
    {
        cout<<"Person(int age,string name):_age(age),_name(name)"<<endl;
    }
    //用于访问私有变量
    void set_name(const char* name)
    {
        _name = name;
    }
    string get_name()
    {
        return _name;
    }
    void set_age(int age)
    {
        _age = age;
    }
    int get_age()
    {
        return _age;
    }
protected:
    int _age;       //年龄
private:
    string _name;   //名字
};

//Student 派生类
class Student : public Person
{
public:
    Student()
    {cout<<"Student()"<<endl;}
    //在派生类中可以指定调用基类中的构造函数
    Student(int age,const char *name): Person(age,name)
    {
        cout<<"Student(int age,const char *name): Person(age,name)"<<endl;
    }
    ~Student()
    {cout<<"~Student()"<<endl;}
};

int main() {
    Student b(10,"lisi");
    cout<<b.get_age()<<endl;
    cout<<b.get_name()<<endl;
    return 0;
}

输出:先调用基类带参数构造函数,在调用派生类带参数构造函数

Person(int age,string name):_age(age),_name(name)
Student(int age,const char *name): Person(age,name)
10
lisi
~Student()
~Person()

六、派生类中的成员变量与基类中的成员变量名冲突(尽量避免这种情况发生

//基类
class A
{
public:
    A():a_x(10),a_y(20)
    {}
    int a_x;
    int a_y;
};

//派生类
class B : public A
{
public:
    B():a_x(20),b_y(20) //a_x一定是B类的
    {
        //可以在里面实现访问基类中的同名成员变量
        A::a_x = 20;//将基类中的同名成员修改为20
    }
    int a_x;//和基类中的成员变量名字冲突
    int b_y;
};

int main() {
    //派生类会不会继承类中同名的属性呢?可以继承的!
    cout<<sizeof(B)<<endl;//16 说明可以继承
    cout<<sizeof(A)<<endl;//8

    B b;
    //b.a_x:派生类中的成员变量还是继承自基类的成员变量?
    cout<<b.a_x<<endl;//20 说明是派生类的
    //访问基类中的同名的成员变量
    cout<<b.A::a_x<<endl;//20  基类名::基类中的成员变量
    return 0;
}

总结:

1、当子类成员和父类成员同名时,子类依然从父类继承同名成员

 2、如果子类有成员和父类同名,子类访问其成员默认访问子类的成员(本作用域,就近原则)

 3、在子类通过作用域::进行同名成员区分(在派生类中使用基类的同名成员,显示使用类名限制符)

 七、隐藏

1、子类函数与父类函数的名称相同,参数也相同,但是父类函数没有virtual,父类函数被隐藏

//基类
class A
{
public:
    void func()
    {
        cout<<"A::func"<<endl;
    }
};

//派生类
class B : public A
{
public:
    //和基类中的成员函数同名,参数也相同 隐藏基类同名函数
    void func()
    {
        cout<<"B::func"<<endl;
    }
};

int main() {
    B b;
    b.func();   //B::func
    b.A::func();//A::func
    return 0;
}

2、子类的函数与父类的名称相同,但是参数不同,父类函数被隐藏

//基类
class A
{
public:
    void func()
    {
        cout<<"A::func"<<endl;
    }
};

//派生类
class B : public A
{
public:
    //和基类中的成员函数同名,参数不相同
    void func(int x)
    {
        cout<<"B::func(int x)"<<endl;
    }
};

int main() {
    B b;
    b.func(10);   //B::func(int x)
    return 0;
}

八、多继承、环形继承、虚继承

1、C++中的多继承是指一个类可以同时继承多个基类的特性和行为

class Base1 {
    // Base1的成员
};

class Base2 {
    // Base2的成员
};
class Base : public Base1, public Base2 
{ 
        // Derived的成员
};
Base类继承了Base1和Base2的公共和私有成员(但请注意,私有成员在派生类中仍然保持私有)。这意味着Base类的实例将能够访问Base1和Base2中的公共成员和Base自己定义的成员。

2、C++中的环形继承是指在类的继承关系中形成了一个循环,即类A继承自类B,类B又继承自类A或类B继承自类C,类C又继承自类A,这样就形成了一个闭环。例如:

//间接基类
class A
{
public:
    int a;
};
//直接基类
class B : public A
{
public:
    //int a;
    int b;
};
//直接基类
class C : public A
{
public:
    //int a;
    int c;
};
//派生类
class D: public B,public C
{
    //来自B
    //int a;
    //int b;

    //来自C
    //int a;
    //int c;
    int d;
};
int main() {
    D d;
    d.a = 10;   //error: request for member 'a' is ambiguous
    cout<<sizeof(D)<<endl; //8+8+4=20
    return 0;
}

出现问题,D类初始化来自A类的成员变量是报错,编译器无法确定成员的访问路径。

3、C++中的虚继承是一种解决多重继承中菱形继承问题的方法

        虚继承通过在继承列表中使用关键字virtual来指定某个基类是以虚继承的方式被继承。这样,所有派生自同一个虚基类的类将共享一个基类对象的实例,避免了上述环形继承出现的重复和歧义问题。

//虚基类
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;};

九、静态成员的继承

        静态成员的继承行为与非静态成员有所不同。当一个派生类继承自一个包含静态成员的基类时,基类中的静态成员不会创建新的副本,而是被派生类共享。这意味着派生类中访问静态成员时,实际上是访问的基类中的静态成员。

class A {
public:
    static int static_member; // 静态成员声明
    static int func();
};
int A::static_member = 100; // 静态成员定义

class B : public A {
public:

};

int main() {
    B b;
    b.static_member = 200;
    cout<<A::static_member<<endl; //200 说明静态成员变量是可以进行继承的
    cout<<B::static_member<<endl; //200
    return 0;
}
  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值