C++ 3种继承方式及应用场景

735 阅读7分钟

前言

c++是支持多继承的,这点和java单继承不一样,但本小节只讲单继承的情况。在c++中派生类(子类)是可以继承基类(父类)的publicprotected类型的成员,这一点和java是一致的。此外C++提供了三种继承方式,公有继承、私有继承和保护继承。继承方式会影响子类的对外访问作用域。 这一点大家听起来可能有点迷糊,耐心往下看,本文就是帮助大家理解这个新概念的。

C++继承的语法格式

class 派生类 : 继承类型 基类

//demo ,这里可以是public,protected,private中的一个
class Child : public Base

公有(public)继承

公有继承使用关键字 public 来声明。在公有继承中,派生类继承了基类的公有成员保护成员,但不继承基类的私有成员。父类成员在子类中保持原有的访问级别。

class Base {
public:
    int x;
protected:
    int y;
private:
    int z;
}; 

子类public继承

class Derived : public Base {
    void func() {
        x = 100;
        y = 100;
//        z = 100; //子类无法访问父类的private成员
    }
};
  
//子类只能继承x,y  

外部调用

int main{  
    Derived derived;  
    derived.x = 100; //可以访问  
    derived.y = 100; //无法访问  
    derived.z = 100; //无法访问  
  
    return 0;  
}  

对象只能访问类的public属性或者方法,也就说这里只能访问x。为什么外部只能访问x呢?因为使用public继承,子类继承了基类的成员之后,访问权限就会变成如下,只是这里使用public继承,所以保持基类原有的访问权限

class Derived : public Base {
public:
    int x;
protected:
    int y;
private:
    int z;
};

保护(protected)继承

保护继承使用关键字 protected 来声明。在保护继承中,派生类继承了基类的公有成员和保护成员,但是对子类以外的访问权限,都变成了派生类的protected继承成员。

class Derived : protected Base {
    void func() {
        x = 100;
        y = 100;
//        z = 100; //子类无法访问父类的private成员
    }
};
  
//子类只能继承x,y,这个和3种继承方式没关系  
  
int main{  
    Derived derived;  
    derived.x = 100; //无法访问  
    derived.y = 100; //无法访问  
    derived.z = 100; //无法访问  
  
    return 0;  
}  

子类继承了x,y属性,可以在子类内部访问,但是通过外部对象访问的时候,x,y都变成protected,所以无法访问,继承后伪代码如下:

class Derived : protected Base {
protected:
    int x;
protected:
    int y;
private:
    int z;
};

私有(private)继承

私有继承使用关键字 private 来声明。在私有继承中,派生类继承了基类的公有成员和保护成员,但是对子类以外的访问权限,都变成了私有成员,无法在派生类外部访问。

class Derived : private Base {
    void func() {
        x = 100;
        y = 100;
//        z = 100; //子类无法访问父类的private成员
    }
};
  
//子类只能继承x,y,这个和3种继承方式没关系  
  
int main{  
    Derived derived;  
    derived.x = 100; //无法访问  
    derived.y = 100; //无法访问  
    derived.z = 100; //无法访问  
  
    return 0;  
}  

子类继承了x,y属性可以在类内部访问,但是通过对象访问的时候,x,y都变成private,所以无法访问, 继承后伪代码如下:

class Derived : protected Base {
protected:
    int x;
protected:
    int y;
private:
    int z;
};

小结

  1. public继承:父类成员在子类中保持原有的访问级别;
  2. private继承:父类成员在子类中变为private成员;
  3. protect继承:父类中public成员会变成protected,父类中protected成员仍然为protected成员,父类中private成员仍然为private成员;

image.png

3种继承方式的应用场景

公有继承的应用

C++中的公有继承适用于以下场景:

  1. 实现"is-a"关系:公有继承用于表示派生类是基类的一种特殊类型。例如,基类可以是Animal,派生类可以是Cat。这种关系表示派生类具有基类的所有特性和行为。

  2. 代码重用:公有继承可以通过继承基类的publoicprotected成员来实现代码的重用。当然这一点并非public继承特有的,而是继承都有的。

  3. 多态性:公有继承是实现多态性的基础。通过基类指针或引用指向派生类对象,可以实现运行时多态性,即根据对象的实际类型调用相应的函数。

  4. 框架和库的设计:在设计框架或库时,可以使用公有继承来定义基类,并让用户通过派生类来扩展和定制功能。用户可以继承基类并重写其中的虚函数,以实现自定义的行为。

class Media {
public:
   virtual void play() {
       cout << "Playing media" << endl;
   }
};

class Audio : public Media {
public:
   void play() override {
       cout << "Playing audio" << endl;
   }
};

int main() {
   Media *media = new Audio();
   media->play();  // 输出 "Playing audio"
   delete media;
   return 0;
}

私有继承(private inheritance):

C++私有继承用于特定的场景,如下所示:

  1. 实现细节隐藏:私有继承可以用于隐藏基类的实现细节。当一个类想要使用基类的功能,但不希望暴露基类的接口给外部时,可以选择私有继承。这样,派生类可以在内部使用基类的成员函数和数据,但对外部来说是不可见的。
  2. 实现"has-a"关系:私有继承可以用于实现"has-a"关系,即派生类具有基类对象作为其成员。这样,派生类可以通过私有继承来获得基类的功能,并将其作为自己的一部分。这种关系通常用于组合或聚合的场景,而不是纯粹的继承。
  3. 重用实现:私有继承可以用于重用基类的实现。当一个类需要在另一个类的基础上进行扩展或修改,而不仅仅是为了继承其接口时,私有继承可以提供一种灵活的方式。派生类可以重写基类的方法,或者添加自己的成员函数和数据,以满足特定的需求。
  4. 需要注意的是,私有继承并不是最常用的继承方式,因为它的使用场景相对较少。在大多数情况下,公有继承或保护继承更常见和更合适。私有继承应该谨慎使用,只在特定的情况下才选择它。
// 基类:媒体播放器
class MediaPlayer {
public:
    void play() {
        cout << "Playing media" << endl;
        // 实现媒体播放的逻辑
    }
};

// 派生类:音频播放器
class AudioPlayer : private MediaPlayer {
public:
    void playAudio() {
        // 使用基类的功能
        play();
        cout << "Playing audio" << endl;
        // 实现音频播放的逻辑
    }
};

// 派生类:视频播放器
class VideoPlayer : private MediaPlayer {
public:
    void playVideo() {
        // 使用基类的功能
        play();
        cout << "Playing video" << endl;
        // 实现视频播放的逻辑
    }
};

int main() {
    AudioPlayer audioPlayer;
    audioPlayer.playAudio();

    VideoPlayer videoPlayer;
    videoPlayer.playVideo();
    return 0;
}

由于使用了私有继承,外部无法直接访问派生类对象的基类成员函数 play() ,这样实现了对基类实现细节的隐藏。同时,派生类和基类之间实现了"has-a"关系,即派生类拥有一个基类对象作为其成员。

保护继承(protected inheritance):

  1. 实现"是一个"关系:保护继承可以用于实现"是一个"关系,即派生类是基类的一种特殊类型。
  2. 实现信息隐藏:保护继承可以用于隐藏基类的实现细节,只暴露部分接口给外部。派生类可以在内部使用基类的保护成员,但对外部来说是不可见的。
// 基类:媒体
class Media {
protected:
   string name;
public:
   Media(const string &name) : name(name) {}

   void play() {
       cout << "Playing media: " << name << endl;
   }
};

// 派生类:音频
class Audio : protected Media {
public:
   Audio(const string &name) : Media(name) {}

   void playAudio() {
       play();
       cout << "Playing audio: " << name << endl;
   }
};

int main() {
   Audio audio("Audio File");
   audio.playAudio(); // 输出:Playing media: Audio File, Playing audio: Audio File

   return 0;
}

由于保护继承的特性,基类的保护成员 name 对于外部类和其他派生类来说是不可访问的。只有派生类内部可以访问和使用这些保护成员。