C++ 继承&隐藏&菱形继承

一、继承
继承是c++中的一种机制,是面向对象复用的重要手段。通过继承机制,可以利用已有的类来定义新的类,新的类不仅拥有新的成员,同时也拥有旧的成员。把已存在的类称为父类或基类,新定义的类称为子类或派生类,继承是类之间的关系建模。

class Person{  //Person是父类
public:
    Person(const string& name)        
    : _name(name)
    {}
void show{
    cout<<name<<endl;
}
protected:
    string _name;
};
class Student:public Person{ //Student是子类,public是继承关系
protected:
    int _num;
}

继承方式有三种:
1.公有继承(public)

#include<iostream>
#include<string>
using namespace std;

class Student
{
public :
    Student(string n, int g,int a)
    {
        name = n;
        grade = g;
        age = a;
    }
    void show()
    {
        cout << "Student:" << endl;
        cout << "name=" << name << endl;
        cout << "grade=" << grade<<endl;
        cout << "age=" << age << endl;
    }
protected:
    string name;
    int grade;
private:
    int age;
};

class GraduateStudent :public Student                       
{
public:
    GraduateStudent(string n, int g, int a) :Student(n, g, a)   //调用基类的构造函数,构造基类
    {
        cout << "GraduateStudent" << endl;
    } 

    void show1()
    {
        cout << "GraduateStudent:" << endl;                           
        cout << "name= " << name << endl;
        cout << "grade= " << grade << endl;
    }

};

void main()
{
    GraduateStudent g("xiaoming", 9, 15);
    g.show();                 //子类可以直接访问基类的公共成员
    g.show1();
}

公有继承会把父类的公有成员继承到子类的公有成员,保护成员会变成子类的保护成员,私有成员子类不可以访问。
结论:
父类的公有成员可继承为子类的公有成员,在子类和外部都可以访问;
父类的受保护成员继承为子类的受保护成员,只有在子类可访问;
父类的私有成员,子类不可以访问。

2.保护继承(protected)

class Student
{
public :
    Student(string n, int g,int a)
    {
        name = n;
        grade = g;
        age = a;
    }
    void show()
    {
        cout << "Student:" << endl;
        cout << "name=" << name << endl;
        cout << "grade=" << grade<<endl;
        cout << "age=" << age << endl;
    }

    string name;   //公有
    int grade;
private:
    int age;
};

class GraduateStudent :protected Student                        
{
public:
    GraduateStudent(string n, int g, int a) :Student(n, g, a)   //调用基类的构造函数,构造基类
    {
        cout << "GraduateStudent" << endl;
    } 

    void show1()
    {
        cout << "GraduateStudent:" << endl;                           
        cout << "name= " << name << endl;
        cout << "grade= " << grade << endl;
    }

};

void main()
{
    GraduateStudent g("xiaoming", 9, 15);
    g.show();   //出错,无法直接访问子类的公有成员
    g.print1();
}

保护继承会把父类的公有成员或保护成员都变成子类的保护成员,但对于私有成员,子类仍不可以访问。
结论:
父类的公有成员,子类继承为保护成员,只能在子类访问;
父类的保护成员,子类继承为保护成员,只能在子类访问;
父类的私有成员,子类不可以访问。

3.私有继承(private)
父类的公有成员,子类继承为私有成员,只能在子类访问;
父类的保护成员,子类继承为私有成员,只能在子类访问;
父类的私有成员,子类不可以访问。

不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,但是基类的私有成员在子类中不能访问。
public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。
protetced/private继承是一个实现继承,基类的部分成员并未完全成为子类接口的一部分,是 has-a 的关系原则,在非特殊情况下不会使用这两种继承关系,绝大多数的场景下使用的都是公有继承。

二、派生类的默认成员函数
类的默认成员函数包括:拷贝构造函数、构造函数、析构函数、赋值操作符重载、取地址操作符重载、const修饰的取地址操作符重载。
在继承关系里面,如果派生类没有显示定义这六个函数,那么编译系统会自动合成这六个默认成员函数。

三、隐藏(重定义)
隐藏是指子类隐藏了父类的函数,特点是:
1)子类函数与父类函数的名称相同,参数不同,则父类函数被隐藏,此时的父类函数有无virtual修饰均可构成隐藏;
2)子类函数与父类函数的名称、参数相同,但父类函数没有virtual,则父类函数被隐藏(若有virtual修饰,是构成覆盖)。

与隐藏定义相似的是覆盖(重写),覆盖是指子类中的函数,其函数名、参数列表、返回值类型都与父类中被类中被覆盖的函数相同,且
父类中被覆盖的函数必须有virtual修饰。
覆盖的特点为:
1)子类函数与父类函数不在同一作用域,分别位于子类和父类;
2)函数名、参数类型相同;
3)父类中被覆盖的函数是虚函数。

用一代码说明:

class father
{
public:
    void show1()
    {
        cout << "father::show1" << endl;
    }

    virtual void show2()
    {
        cout << "father::show2" << endl;
    }
};

class son:public father
{
public:
    void show1()
    {
        cout << "son::show1" << endl;
    }

    void show2()
    {
        cout << "son::show2" << endl;
    }
    void show2(string s)
    {
        cout << "son::void show2(string s)" << endl;
    }
};

int main()
{
  son s;
  s.show1();
  s.show2();
  s.show2("aaa");
  return 0;
}

主函数中第一句执行语句是隐藏(函数名、参数相同,父类函数没有virtual);第二句是覆盖(函数名、参数相同,且有virtual修饰父类函数);第三句是隐藏(函数名相同,参数不同)。

四、菱形继承
这里写图片描述
如上图,B、C继承A,D继承B、C,这个关系就是菱形继承。

class A 
{  
protected:  
    int _a;  
public:  
    void show()  
    {  
        cout << "A::show" << endl;  
    }  
};  

class B:public A  
{  
protected:  
    int _b;  
};  

class C : public A  
{  
protected:  
    int _c;  
};  
class D :public B, public C  
{  
private:  
    int _d;  
};  
int main()  
{  
    D d;  
    d.show();//报错,调用不明确    
} 

D的对象模型中保存了两份A,当调用A中的函数show时,就会出现调用不明确的情况,不仅是数据冗余而且还浪费空间。这就是菱形继承中经常出现的问题:数据冗余和二义性。c++中通常使用虚继承来解决这两个问题。

虚继承:
这里写图片描述
如图,虚继承就是在B、C在继承A时,在它们前面加上virtual关键字。
使用虚继承之后,B、C中就不是保存A的内容,而是保存了一份偏移量地址,并且A是被放在了一块公共的空间,这样做就降低了数据冗余性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值