猫猫哥

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
//############################################################################
/*
 * 公有,保护,私有继承
 */

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. 小心函数覆写时的错字
 */
posted on 2018-12-25 00:05  猫猫哥  阅读(173)  评论(0编辑  收藏  举报