前言
c++是支持多继承的,这点和java单继承不一样,但本小节只讲单继承的情况。在c++中派生类(子类)是可以继承基类(父类)的public和protected类型的成员,这一点和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;
};
小结
- public继承:父类成员在子类中保持原有的访问级别;
- private继承:父类成员在子类中变为private成员;
- protect继承:父类中public成员会变成protected,父类中protected成员仍然为protected成员,父类中private成员仍然为private成员;
3种继承方式的应用场景
公有继承的应用
C++中的公有继承适用于以下场景:
-
实现"is-a"关系:公有继承用于表示派生类是基类的一种特殊类型。例如,基类可以是Animal,派生类可以是Cat。这种关系表示派生类具有基类的所有特性和行为。
-
代码重用:公有继承可以通过继承基类的
publoic和protected成员来实现代码的重用。当然这一点并非public继承特有的,而是继承都有的。 -
多态性:公有继承是实现多态性的基础。通过基类指针或引用指向派生类对象,可以实现运行时多态性,即根据对象的实际类型调用相应的函数。
-
框架和库的设计:在设计框架或库时,可以使用公有继承来定义基类,并让用户通过派生类来扩展和定制功能。用户可以继承基类并重写其中的虚函数,以实现自定义的行为。
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++私有继承用于特定的场景,如下所示:
- 实现细节隐藏:私有继承可以用于隐藏基类的实现细节。当一个类想要使用基类的功能,但不希望暴露基类的接口给外部时,可以选择私有继承。这样,派生类可以在内部使用基类的成员函数和数据,但对外部来说是不可见的。
- 实现"has-a"关系:私有继承可以用于实现"has-a"关系,即派生类具有基类对象作为其成员。这样,派生类可以通过私有继承来获得基类的功能,并将其作为自己的一部分。这种关系通常用于组合或聚合的场景,而不是纯粹的继承。
- 重用实现:私有继承可以用于重用基类的实现。当一个类需要在另一个类的基础上进行扩展或修改,而不仅仅是为了继承其接口时,私有继承可以提供一种灵活的方式。派生类可以重写基类的方法,或者添加自己的成员函数和数据,以满足特定的需求。
- 需要注意的是,私有继承并不是最常用的继承方式,因为它的使用场景相对较少。在大多数情况下,公有继承或保护继承更常见和更合适。私有继承应该谨慎使用,只在特定的情况下才选择它。
// 基类:媒体播放器
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):
- 实现"是一个"关系:保护继承可以用于实现"是一个"关系,即派生类是基类的一种特殊类型。
- 实现信息隐藏:保护继承可以用于隐藏基类的实现细节,只暴露部分接口给外部。派生类可以在内部使用基类的保护成员,但对外部来说是不可见的。
// 基类:媒体
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 对于外部类和其他派生类来说是不可访问的。只有派生类内部可以访问和使用这些保护成员。