C++进阶--类的继承

//############################################################################
/*
 * 公有,保护,私有继承
 */

class B {
};
class D_priv : private   B { };        //私有继承
class D_prot : protected B { };     //保护继承
class D_pub : public    B { };        //公有继承

/*
不同的继承方法指定了派生类对基类不同的访问控制权限

上述三个派生类:
1. 都不能访问B中的私有成员.
2. D_pub继承B中的公有成员为公有,继承B中的保护成员为保护
3. D_priv继承B中的公有保护成员为私有
4. D_prot继承B中的公有保护成员为保护

转换:
1. 任何人都可以将一个D_pub*转换为B*. D_pub是B的特殊情况
2. D_priv的成员和友元可以将D_priv*转成B*
3. D_prot的成员,友元及子女可以将D_prot*转成B*

注意:只有公有继承是is-a的关系
*/

/* 详细实例 */

class B {
   public:
      void f_pub() { cout << "f_pub is called.\n"; }
   protected:
      void f_prot() { cout << "f_prot is called.\n"; }
   private:
      void f_priv() { cout << "f_priv is called.\n"; }
};

class D_pub : public B {  //对于公有继承
   public:
      void f() {
         f_pub();   // OK. D_pub的公有成员函数
         f_prot();  // OK. D_pub的保护成员函数
         f_priv();  // Error. B的私有成员函数
      }
};

class D_priv : private   B { //对于私有继承
   public:
   using B::f_pub; //使其在D_priv的作用域内可见
      void f() {
         f_pub();   // OK. D_priv的私有成员函数
         f_prot();  // OK. D_priv 的私有成员函数
         f_priv();  // Error. B的私有成员函数
      }
};

int main() {
   D_pub D1;
   D1.f_pub();  // OK. f_pub()是D_pub的公有成员函数

   D_priv D2;
   D2.f_pub();  // Error. f_pub()是D_priv的私有成员函数,增加using B::f_pub之后OK

   B* pB = &D1;  // OK  可以转换
   pB = &D2;     // Error  不能转换
   ...
}

//############################################################################
/*
 * 私有继承: 类似于has-a关系,跟组合类似
 */
class Ring {
   virtual tremble();
   public:
   void tinkle() {...; tremble(); }
};

/* 组合 */
class Dog {
   Ring m_ring;
   public:
   void tinkle() {m_ring.tinkle();}  // 向前调用
};

/* 私有继承*/
class Dog_Priv : private Ring {
   public:
   using Ring::tinkle;
};

/*
 * 组合的优点: 比如可以有多个ring,ring可以切换 //通常情况下倾向于组合,更低耦合,更灵活
 * 私有继承的优点:更优雅的多态,比如
 *
 * 在ring类中增加虚函数tremble(), 该函数在tinkle中被调用
 */

/*
 * 公有继承 => "is-a" 关系
 *
 * 基类能做的任何事情,派生类需要也能做
 */

//像以下类的设计就是不合适的
class Bird {
   public:
   void fly();
};

class Penguin : public Bird {};

Penguin p;
p.fly();

// class flyableBird : public bird {};
//   public:
//      void fly();
//penguin p;
//p.fly();

// 看几个例子
class Dog {
   public:
      void bark() { cout << "Whoof, I am just a dog.\n";};
};

class Yellowdog : public Dog{
   public:
      void bark() { cout << "Whoof, I am a yellow dog.\n";};
};

int main() {
   Yellowdog* py = new Yellowdog();
   py->bark();
   Dog* pd = py;
   pd->bark();
}

OUTPUT:
Whoof, I am a yellow dog.
Whoof, I am just a dog.
/*
 * 结论:不要覆写非虚函数
 */

class Dog {
   public:
   void bark(int age) { cout << "I am " << age; }
   virtual void bark(string msg = "just a" ) {
      cout << "Whoof, I am " << msg << " dog." << endl; }
};

class Yellowdog : public Dog {
   public:
   using Dog::bark;
   virtual void bark(string msg="a yellow" ) {
      cout << "Whoof, I am " << msg << " dog." << endl; }
};

int main() {
   Yellowdog* py = new Yellowdog();
   py->bark(5);
}

OUTPUT:
Whoof, I am a yellow dog.
Whoof, I am just a dog.

/*
 * 不要重新定义虚函数的默认参数
 *   - 默认参数是静态绑定的
 *   - 虚函数是动态绑定的
 */

/*
 * 在类dog中增加如下函数:
 * virtual void bark(int age) { cout << "I am " << age << " years old"<< endl; }
 * in main(),
 *    py->bark(5);  // 编译不过
 *                  // 可以通过在yellowdog中加"using Dog::bark;"修复  为了保持is-a关系
 */

// 防止意外覆写,或者没有覆写,增加了override关键字
class Dog {
   public:
      virtual void bark() { cout << "I am just a dog.\n";};
      void run();
};

class Yellowdog : public Dog{
   public:
      virtual void barks() { cout << "I am a yellow dog.\n";}; //在旧标准中不会报错,等出错了调试比较困难
};

// C++ 11 standard:
class Yellowdog : public Dog{
   public:
      virtual void barks() override;
            // 编译错误:没有覆写的函数

      virtual void bark() const override;
            // 编译错误:没有覆写的函数

      void run() override;  // 压根不是虚函数,错误
};

/*
 * 避免采坑:
 * 1. 类的精确定义
 * 2. 不要覆写非虚函数
 * 3. 不要覆写虚函数的默认参数
 * 4. 强制继承被遮盖的函数
 * 5. 小心函数覆写时的错字
 */

原文地址:https://www.cnblogs.com/logchen/p/10171771.html

时间: 2024-10-27 06:55:32

C++进阶--类的继承的相关文章

Python 进阶(四)类的继承

继承一个类 如果已经定义了Person类,需要定义新的Student和Teacher类时,可以直接从Person类继承: class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender 定义Student类时,只需要把额外的属性加上,例如score: class Student(Person): def __init__(self, name, gender, score)

【SSH快速进阶】——Hibernate继承映射:每个具体类映射一张表

上篇文章说的是每个类映射一张表,不管是父类还是子类.与上篇文章不同的是,这里是每个"具体类"映射一张表,什么意思呢?就是让每个子类(具体的.有意义的类)映射一张表. 场景 与上篇文章场景一样,如下类图 上图中Pig类和Bird类继承Animal类,要让每个具体类映射一张表,就是只映射Pig和Bird,如下表所示: (表 1) 上面的表有个特点就是,t_pig和t_bird的主键永远都不会相同.因为表面上看起来这是两张表,但实际上存储的都是动物(同一类型),所以还可以看做是一张表. 配置

【Unity】常用脚本类的继承关系(入门篇)

前言 学习Unity开发引擎的初学者会接触大量的脚本类,而这些类之间的关系往往容易被忽略. 本文对Unity引擎开发中的一些常用类及其关系进行了简单的归纳总结. 博文首发地址:http://blog.csdn.net/duzixi Unity常用脚本类继承关系图 对Unity开发稍有了解的朋友都知道,在Unity开发框架中有4个基本层次:工程(应用程序).场景.游戏对象和组件. 在脚本中,整个应用程序和场景由Application类控制:而游戏对象和组件类均继承于Object类. 在Object

【SSH快速进阶】——Hibernate继承映射:每个类映射一张表

上文说了每棵继承树映射一张表,本文继续描述让每个类都映射一张表的配置. 场景 与上篇文章场景一样,如下类图 上图中Pig类和Bird类继承Animal类,要让每个类映射一张表,就是让着三个类都分别映射一张表,但同时这三张表也跟对应的三个类似的,有继承关系,如下表所示: (表 1) Pig和Bird的记录都表示一个动物,因此都会在Animal对应的表中存储,而它们各自的拓展属性则存储在各自对应的表中,主键与Animal对应记录的主键各自对应. 配置 PO对象 跟上篇文章一样,实体类没有任何变化.

【SSH快速进阶】——Hibernate继承映射:每棵继承树映射一张表

我们都知道,Hibernate最大的一个优点就是使开发更加"面向对象",类与类之间有继承关系,Hibernate中也对这种继承关系提供了映射的封装. Hibernate为继承映射提供了三种策略 1.每棵继承树使用一张表 2.每个子类使用一张表 3.每个具体类使用一张表 本文对第一种策略进行说明. 场景 如下类图 上图中Pig类和Bird类继承Animal类,每棵继承树对应一张表,即在同一棵继承树中,所有的类的对象信息(记录)共同存放到一张表中,要判断某条记录属于哪个对象,需要在表中添加

Javascript 进阶 面向对象编程 继承的一个例子

Javascript的难点就是面向对象编程,上一篇介绍了Javascript的两种继承方式:Javascript 进阶 继承,这篇使用一个例子来展示js如何面向对象编程,以及如何基于类实现继承. 1.利用面向对象的写法,实现下面这个功能,实时更新数据的一个例子: 2.使用对上面类的继承,完成下面的效果: 好了,不多说,js的训练全靠敲,所以如果觉得面向对象不是很扎实,可以照着敲一个,如果觉得很扎实了,提供了效果图,可以自己写试试. 1.第一个效果图代码: [javascript] view pl

Objective-C编程 — 类和继承

讲述面向对象中的一个重要概念——继承,使用继承 可以方便地在已有类的基础上进行扩展,定义一个具有父 类全部功能的新类. 父类和子类 我们在定义一个新类的时候,经常会遇到要定义的新类是某个类的扩展或者是对某个类的修正 这种情况.如果可以在已有类的基础上追加内容来定义新类,那么新类的定义将会变得更简单. 像这种通过扩展或者修改既有类来定义新类的方法叫作 继承 (inheritance).在继承关系中,被继 承的类称为 父类 (superclass),通过继承关系新建的类称为 子类 (subclass

lua-table类的继承

--男人类man = {name = "man",age=123}--继承空间man.__index=man--儿童类child= {}--继承setmetatable(child,man)print(child.age)yong= {}setmetatable(yong,man)child.age=12yong.age=16print("青年年龄:"..yong.age..",儿童团年龄:"..child.age)print(man.age)

类与继承(一)

在Java中,类文件是以.java为后缀的代码文件,在每个类文件中最多只允许出现一个public类,当有public类的时候,类文件的名称必须和public类的名称相同,若不存在public,则类文件的名称可以为任意的名称(当然以数字开头的名称是不允许的). 在类内部,对于成员变量,如果在定义的时候没有进行显示的赋值初始化,则Java会保证类的每个成员变量都得到恰当的初始化: 1)对于  char.short.byte.int.long.float.double等基本数据类型的变量来说会默认初始