• LeekinDeveloper@Gmail.com

C++继承<单继承>


1. 什么是继承

C++的继承是opp面向对象的一大重点,他能让你提高编程的效率,让你少写重复代码,如果你拥有面向对象的思维的话,继承通俗的来说就像现实生活当中,我们继承继承了父亲的财产,那么单继承就是:国家计划生育,只能生育一个,但是这其中有很多学问。但在计算机的世界里没有现实社会这么随意,一个简单的单继承例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Friut
{
private:
int ID;
int temp;
string name;
protected:
int getID()const
{

return this->ID;
}
string getName()const
{

return this->name;
}
int getTemp()const
{

return this->temp;
}

public:
Friut(int id,int tmp,string name);

}

//apple 继承Friut
calss apple : public Friut
{
public:
typedef Friut super;
apple(bool isEat,int id,int temp,string name):super(id,temp,name)
{
this->isEat=isEat;
}
bool isEat;
void appPrint()
{

printf(isEat,ID,temp,name);
}
}

说明:这里我们建立了单继承,calss apple : public Friut告诉我们apple继承自Friut,所以基类是Friut,派生类是:apple,Friut前面的public告诉我们这是一个公有继承,将建立is-a关系

2. 继承的访问权限控制

就以上面的例子来说明,apple继承自Friut,那么apple得到了Friut当中protected和public权限的数据,被声明为private的数据将继承不到,但是可以通过基类当中的protected权限块来访问,关于访问权限的控制,需要根据实际情况来确认,是用private还是protected或public?下面是基类当中访问权限控制下派生类可继承信息:

  • 声明为private:私有,派生类将无法继承
  • 声明为protected:受保护的,派生类可以继承这部分的数据,但是它对外不可见。
  • 声明为public:公有的,派生类可以继承到,而且对外可见,任何类都将能访问。

3. 构造函数

根据上面的例子:apple(bool isEat,int id,int temp,string name):super(id,temp,name)可知,创建派生类对象时,程序首先构造基类对象,我们通过构造函数将数据传递给了基类构造函数,例如:

1
apple bigApple(false,1001,20,"bigApple");

在使用apple的构造函数创建对象时,首先将实参传递给super(id,temp,name)形参,并创建了基类对象,接下来执行完{…}模块,bigApple创建完成。

假如我们没有加入成员初始化列表:super(id,temp,name)语句呢。

1
2
3
4
5
//class apple
apple(bool isEat,int id,int temp,string name)
{
this->isEat=isEat;
}

这时程序将使用基类的默认构造函数来创建对象,在创建派生类对象。有关派生类构造函数的要点如下:

  • 首先创建基类对象
  • 派生类构造函数应当通过成员初始化列表将基类信息传递给基类的构造函数
  • 派生类构造函数应当初始化派生类新增的数据成员

除非要使用默认构造函数,否则应当显示的指明基类构造函数,比如:

1
2
3
4
5
//class apple
apple(bool isEat,const Friut &friut):Friut(friut)
{
this->isEat=isEat;
}

这里的Friut(friut)将使用friut创建一个基类Friut对象,由于friut的类型是const Friut &,所以将使用基类的拷贝构造函数,但是我们并没有定义Friut的拷贝构造函数,需要用到而又没有定义,这时候编译器将为我们生成一个拷贝构造函数。

4. 派生类和基类之间的特殊关系<难点>

1)派生类对象可以使用基类的方法,条件是方法不是私有的

1
2
apple smallApple(true,1002,30,"smallApple");
smallApple.getName(); //派生类对象smallApple使用基类方法getName

2)基类指针可以在不通过显示转换的情况下指向派生类对象,基类引用可以在不通过显示转换的情况下引用派生类对象

1
2
3
4
5
apple redApple(false,10011,23,"redApple");
Friut &friut1 = redApple;
Friut *friut2 = &redApple;
friut1.getID();
friut2->getTemp();

3)基类对象的指针和引用只能调用基类的方法,而不能调用派生类方法appPrint(),如下:

1
2
friut1.appPrint();//这将是不引许的
friut2->appPrint();//这将是不引许的

4)派生类的指针和引用不能指向和引用基类对象,以下操作将是不引许的

1
2
3
Friut noFriut(1014,29,"without_");
apple &app1 = noFriut;//不引许的
apple *ptr_app = &noFriut;//不引许的

5. 使用公有继承建立is-a关系

继承要和面向对象思想结合使用,这将是有意义的,不仅仅满足is-a关系就行了.

  • 公有继承不能建立has-a关系,比如午餐可能包含水果,但午餐并不是水果,所以不能从Friut类当中派生出Lunch类;

  • 公有继承不能建立is-like-a关系,比如语文当中会将一个发呆的人比作木头人,但其实他并不真的是木头,所以不能从wood当中派生出people类;

  • 公有继承不能建立is-implemented-as-a(作为…..来实现),例如,可以使用数组来实现栈,但从Array类中派生出Stack是不适合的,因为栈不是数组。

  • 公有继承不能建立uses-a关系,比如,电脑可以使用打印机,但是不能从电脑中派生出打印机机类,这将是无意义的,

oop面向对象有助于为我们把计算机当中非常抽象的东西在现实生活中找到例子,大大提高我们对抽象概念的理解,这更接近人的思维,更容易理解,所以继承也要和面向对象相结合才能够发挥最大效用,同时也增强了代码的可读性,适合团队协作项目。