首页 > 代码库 > C++中类的继承关系梳理

C++中类的继承关系梳理

人生苟且了很长时间,需要再继续努力了。

总结了C++的继承方面的关系:

  朋友在面试的时候被问过一个问题,说类的继承重要的一点是什么,他没有答到点子上,后来面试官提到的是代码的复用,不用每次都重新写相同的代码,还是有道理的。

类的声明:

class TableTennisPlayer
{
private:
  string firstname;
  string lastname;
  bool hashTable;

public:
  TableTennisPlayer(const string &fn = "none", const string &ln = "none", bool ht=false);
  void Name() const; //只读的一个函数
  bool HashTable() const {return hashTable;};
  bool ResetTable (bool v) { hashTable = v;};

};

初始化:

TableTennisPlayer play1("zhang", "jingle", true);

 string firstname;
 string lastname;
 bool   hashTable;

原型是这样的

TableTennisPlayer(const string &fn = "none", const string &ln = "none", bool ht=false);

由此引申出来的一点,写字符串的过程中的赋值过程,应该是调用了 string 类的 以const char *作为参数的构造函数。如果是以string 类为参数的话,则会调用 

等等,有一点没有想明白,形参为const string &, 如果传递的参数类型的是string 的话,调用的构造函数是几次?,如果传入的参数类型是const char*的话,调用的构造参数又是几次?

我感觉这方面还是没有理清楚??

在派生一个类中,如下所示,增加了一个成员变量,对于

class RatedPlayer: public TableTennisPlayer
{
private:
  unsigned int rating;
public:
  //想起来了,是基本的构造函数
  RatedPlayer(unsigned int r = 0, const string &fn = "none", const string &ln = "none", bool ht = false);

  //是复制构造函数
  RatedPlayer(unsigned int r, const TableTennisPlayer &tp);

  unsigned int Rating() const {return rating;};
  void Reseat (unsigned int r){rating = r;};  
};

派生类的构造函数,

1、首先创建基类对象

2、派生类的构造函数通过成员初始化列表将基类的信息传递给基类构造函数。(这个分情况,可能调用基类的构造函数,默认构造函数和复制构造函数)

3、派生类的构造函数应初始化派生类新增的数据成员。

 

基类的私有部分也是派生类的一部分,但是只能通过基类的公有和保护方法来进行访问。访问权限也是很重要的。

 

基类和派生类的关系:

1、派生类可以使用基类的方法,这个方法不能是私有的。

2、基类指针可以在不进行显示转换的情况下指向派生类,基类引用可以在不显示转换的情况下,引用派生类对象(向上强制转换,不能将派生类的指针指向派生类)

但是上面基类的指针却只能调用基类的方法。

例子:

class base

{

};

class bigger::public base

{

}

bigger hh;

base &rt = hh;

base *rt2 = &hh;

就是这么简单。

基类指针如果想要调用派生类的方法

1、虚函数

virtual void test()

{

 printf("test.....");

};

如果只是虚函数的话,在基类中是要实现的。

在派生类中可以不用重新实现这个类,如果在派生类中没有实现,则调用基类的方法。如果在派生类重新实现了该方法,则调用的时候使用派生类的方法。

2、纯虚函数

纯虚函数 virtual  test() = 0;

这个是需要在派生类中必须要实现的,带有纯虚函数的基类是不能直接使用的,只是作为一个框架在使用,目的就是让别人继承实现多态。

class base

{

public:

  virtual dddd() = 0;

};

base aaa; 这样是不可以的。

class bigger ::public base

{

public:

  dddd()

  {

     xxxxxxxxxx;

  }

};

 bigger bbbb ;这样是可以的。

 

C++中,多态的这种机制才是精华,访问权限的控制。

如果基类中的数据和方法是privated:

在派生类中,只能调用 基类的方法来访问基类的访问权限

虚函数的几种情况,

1、基类中为虚函数

基类中对虚函数有定义,有实现。

想验证一下那种是错误的写法:

1.1 派生类 中 对虚函数有定义,带virtual  无实现

不可以。

1.2派生类中对虚函数有定义,带virtual 有实现

调用派生类中的方法。

1.3派生类中对虚函数有定义,不带virtual 无实现

不可以

1.4 派生类中对虚函数有定义,不带virtual 有实现

调用派生类中方法。

1.4 派生类中对虚函数无定义,不实现。

结果是调用基类的方法。这个是最基本的。

总结一下就是想要在派生类中修改就一定要有定义和实现,感觉自己写的是废话,但是定义带不带virtual 是不是一样呢??

自己纠结了这么长时间的问题,百度了一下就知道了,不是必须的,但是为了代码的清晰易读,还是加上吧,为了让孙子类知道,这个是虚函数,呵呵

 

2、基类中为纯虚函数。

基类中不需要实现,但是在派生类中必须要实现。

3、 当虚函数遇上重载。

简单

class dwelling

{

public:    

  virtual void showperks() const ;    

  virtual void showperks(int a)const ;

};

class hovel:public dwelling

{

public:     

  virtual void showperks() const;

     virtual void showperks(int a)const ;//如果此方法注释的话,也不能调用基类中的该方法,基类中其他版本被隐藏了。

};

当重载遇上虚函数时,想要在派生类中对基类中的函数想要修改的话,就必须在派生类中进行全部重新定义,全部实现。

具体见:/learnCpluseplus/inherit/brass/test1.cpp 。

多态的是实现有两种方式:

1、一种就是派生类中重新定义基类方法。

2、使用虚函数。 

在 上面的例子中 用基类指针指向派生类,如果在基类中定义了一个虚函数,并且在派生类中实现的话(派生类中不管实现的为virtual 还是非virtual ),用基类指针调用该函数,则会调用到派生类中的方法。

看的C++中总结的术语:

  如果方法是通过引用或者指针而不是对象调用的,它将确定使用哪种方法,如果没有使用关键字virtual ,程序将只用引用类型或者是指针类型选择方法,如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法。

 

class

派生类中国访问权限的控制:

C++中的权限分为public、protected、private,继承的方式也有三种,分别是public、protected、private这三种方式

通俗讲,派生类中某个属性或者方法的权限,就是基类中的访问权限与继承访问权限的较小集。(也就是数学中的交集)

 

在C++中,编译器对于方法,是静态联编还是动态联编,是跟方法是否定义为虚函数有关系的,编译器对非虚方法使用的是静态联编,对于虚方法是使用动态联编。

 

虚函数的工作原理:
对每一个对象都保存一个隐藏的指针,隐藏的指针指向虚函数表。

如果派生类中对基类中的虚函数进行了重新实现,则派生类的虚函数表保存新的函数的地址,

如果派生类中没有对基类中的虚函数实现,则派生类的虚函数表扔保存基类中的地址。

class base

{

public:

  virtual void printA();

  virtual void printB()

}

class ::public base

{

  void printB();

  virtual void printC();

};

总结一点,虚函数实现机制比较好,但是内存和执行速度会有一定的成本。

 

抽象基类:

这个是一种很高深的能力,在设计基类的时候应该先构建出出编程中所需要的类,以及他们之间的关系,抽象基类更清晰、复杂度更低,在基于组件的编程模型中很常见。

 

继承和内存中的分配,这个刚开始没有搞清楚,当然现在也没有搞清楚。

抽象能力。

C++中类的继承关系梳理