zoukankan      html  css  js  c++  java
  • 私有继承基类函数如何被访问

      C++面向对象中的继承模式有三种:公有继承(关键字public)、保护继承(关键字protected)和私有继承(关键字private)。在开发中以公有继承占绝大多数情况,保护和私继承使用情况很少。写这篇博客介绍私有继承,源于一个问题。下面给大家详细道来。

    一、限制C++接口问题

      有一个Rectangle类,它向外界提供了三个接口,类定义如下:

     1 struct Position
     2 {
     3     int x;
     4     int y;
     5 };
     6 
     7 class Rectangle
     8 {
     9 public:
    10     explicit Rectangle(const Position& pos, int w, int h)
    11         : _pos(pos)
    12         , _w(w)
    13         , _h(h)
    14     {}
    15     int GetWidth() const { return _w; }
    16     int GetHeight() const { return _h; }
    17     void Draw()
    18     {
    19         std::cout << "Rectangle::Draw()
    ";
    20     }
    21 private:
    22     Position _pos;
    23     int _w;
    24     int _h;
    25 };

      现在,我希望编写一个新的类,只让他有Draw的能力。该怎么办?实现的方式多种多样,但是我们用继承的方式实现这个需求。

    二、继承如何限制C++接口

      我们知道C++面向对象中的基础有三种模式,其中公有继承不改变基类的访问属性,所以无法起到限制上面GetWidth和GetHeight接口。保护继承和私有继承都可以限制基类的公共接口被外部直接访问;保护和私有的区别在于,保护的方法可以被子类访问,私有的方法不行。保护和私有继承都可以限制C++接口的能力。代码如下:

     1 class DrawableRectangle : protected Rectangle
     2 {
     3 public:
     4     explicit DrawableRectangle(const Rectangle& rect)
     5         : Rectangle(rect)
     6     {}
     7 };
     8 
     9 int main()
    10 {
    11     Rectangle rect(Position{ 0, 0 }, 10, 10);
    12     DrawableRectangle drawableRect(rect);
    13     drawableRect.Draw();
    14 }

      或者

     1 class DrawableRectangle : private Rectangle
     2 {
     3 public:
     4     explicit DrawableRectangle(const Rectangle& rect)
     5         : Rectangle(rect)
     6     {}
     7 };
     8 
     9 int main()
    10 {
    11     Rectangle rect(Position{ 0, 0 }, 10, 10);
    12     DrawableRectangle drawableRect(rect);
    13     drawableRect.Draw();
    14 }

      当同学们兴致勃勃地编写上面的代码,点击编译的时候,突然发现:

           

      或 

      

      发现编译根本无法正常通过。这样写确实无法正常通过的,这边“坑爹”吗?有的同学马上想到了友元类可以解决这个问题,于是敲动键盘写下如下代码:

     1 class DrawableRectangle : private Rectangle
     2 {
     3 public:
     4     explicit DrawableRectangle(const Rectangle& rect)
     5         : Rectangle(rect)
     6     {}
     7 
     8     friend class Client;
     9 };
    10 
    11 class Client
    12 {
    13 public:
    14     void Test()
    15     {
    16         Rectangle rect(Position{ 0, 0 }, 10, 10);
    17         DrawableRectangle drawableRect(rect);
    18         drawableRect.Draw();
    19     }
    20 };
    21 
    22 int main()
    23 {
    24     Client t;
    25     t.Test();
    26 }

      恭喜你,这样确实能起到限制C++接口的同时,能够访问基类的接口。编译并运行的结构如下: 

          

       但是你却忽略了友元的弊端,破坏封装性,友元确实能够访问类的保护和私有方法或属性,这里是所有哦!例如我还是可以正常访问GetWidth和GetHeight方法:

     1 class Client
     2 {
     3 public:
     4     void Test()
     5     {
     6         Rectangle rect(Position{ 0, 0 }, 10, 10);
     7         DrawableRectangle drawableRect(rect);
     8         drawableRect.Draw();
     9         std::cout << "width<" << drawableRect.GetHeight() 
    10             << "> height<" << drawableRect.GetHeight() << "
    ";
    11 
    12     }
    13 };
    14 
    15 int main()
    16 {
    17     Client t;
    18     t.Test();
    19 }

      

       绕这么大的弯子,还是没有解决我之前提到的问题。是不是这个问题根本就不能用继承解决?

    三、using关键字

      刚开始我也这么认为,但是直到看到如下代码:

     1 class DrawableRectangle : private Rectangle
     2 {
     3 public:
     4     explicit DrawableRectangle(const Rectangle& rect)
     5         : Rectangle(rect)
     6     {}
     7 
     8     using Rectangle::Draw;
     9 };
    10 int main()
    11 {
    12     Rectangle rect(Position{ 0, 0 }, 10, 10);
    13     DrawableRectangle drawableRect(rect);
    14     drawableRect.Draw();
    15     drawableRect.GetWidth();
    16     drawableRect.GetHeight();
    17 }

      

      编译只报GetWidth和GetHeight接口不可访问的错误,并没有报Draw接口。我们屏蔽掉报错的接口,再次编译后运行:

           

      保护或私有继承的子类中,使用这个using关键字指定基类某个方法或属性都可以在外界通过子类自己调用吗?

      基类方法或属性是public:

     1 class Rectangle
     2 {
     3 public:
     4     explicit Rectangle(const Position& pos, int w, int h)
     5         : _pos(pos)
     6         , _w(w)
     7         , _h(h)
     8     {}
     9     int GetWidth() const { return _w; }
    10     int GetHeight() const { return _h; }
    11     void Draw()
    12     {
    13         std::cout << "Rectangle::Draw()
    ";
    14     }
    15     int _a;
    16 private:
    17     Position _pos;
    18     int _w;
    19     int _h;
    20 };
    21 
    22 class DrawableRectangle : private Rectangle
    23 {
    24 public:
    25     explicit DrawableRectangle(const Rectangle& rect)
    26         : Rectangle(rect)
    27     {}
    28 
    29     using Rectangle::Draw;
    30     using Rectangle::_a;
    31 };
    32 int main()
    33 {
    34     Rectangle rect(Position{ 0, 0 }, 10, 10);
    35     DrawableRectangle drawableRect(rect);
    36     drawableRect.Draw();
    37     drawableRect._a = 10;
    38 }

      

       基类方法或属性是protected:

     1 class Rectangle
     2 {
     3 public:
     4     explicit Rectangle(const Position& pos, int w, int h)
     5         : _pos(pos)
     6         , _w(w)
     7         , _h(h)
     8     {}
     9     int GetWidth() const { return _w; }
    10     int GetHeight() const { return _h; }
    11     void Draw()
    12     {
    13         std::cout << "Rectangle::Draw()
    ";
    14     }
    15     int _a;
    16 protected:
    17     void Fn()
    18     {
    19         std::cout << "Rectangle::Fn() protected
    ";
    20     }
    21     int _b;
    22 private:
    23     Position _pos;
    24     int _w;
    25     int _h;
    26 };
    27 
    28 class DrawableRectangle : private Rectangle
    29 {
    30 public:
    31     explicit DrawableRectangle(const Rectangle& rect)
    32         : Rectangle(rect)
    33     {}
    34 
    35     using Rectangle::Draw;
    36     using Rectangle::_a;
    37     using Rectangle::Fn;
    38     using Rectangle::_b;
    39 };
    40 int main()
    41 {
    42     Rectangle rect(Position{ 0, 0 }, 10, 10);
    43     DrawableRectangle drawableRect(rect);
    44     drawableRect.Fn();
    45     drawableRect._b = 10;
    46 
    47 }

      

      基类方法或属性是private:

     1 class Rectangle
     2 {
     3 public:
     4     explicit Rectangle(const Position& pos, int w, int h)
     5         : _pos(pos)
     6         , _w(w)
     7         , _h(h)
     8     {}
     9     int GetWidth() const { return _w; }
    10     int GetHeight() const { return _h; }
    11     void Draw()
    12     {
    13         std::cout << "Rectangle::Draw()
    ";
    14     }
    15     int _a;
    16 protected:
    17     void Fn()
    18     {
    19         std::cout << "Rectangle::Fn() protected
    ";
    20     }
    21     int _b;
    22 private:
    23     void Fn1()
    24     {
    25     }
    26 private:
    27     Position _pos;
    28     int _w;
    29     int _h;
    30 };
    31 
    32 class DrawableRectangle : private Rectangle
    33 {
    34 public:
    35     explicit DrawableRectangle(const Rectangle& rect)
    36         : Rectangle(rect)
    37     {}
    38 
    39     using Rectangle::Draw;
    40     using Rectangle::_a;
    41     using Rectangle::Fn;
    42     using Rectangle::_b;
    43 
    44     using Rectangle::Fn1;
    45     using Rectangle::_w;
    46 };

      

       语法报错,所以说除了private修饰的成员方法和属性外,另外两种都可以通过using关键正常指定外界可以访问的方法或接口。保护继承同私有继承作用类似,这里不再列举处理进行验证。(上面都是通过visual studio2019编译的,对于低版本的,尤其不支持using关键字的编译器就不支持这种用法了)。

      公用继承下,子类和基类是:is-a的关系,即子类是基类的一种类型。(这里不考虑继承抽象类,子类和抽象类是:has-a关系)私有继承不在让子类和基类拥有:is-a的关系,而是变成:has-a的关系。这种继承模式下,能够起到适当屏蔽基类中的公共方法,到达子类不同于基类的行为的作用。

    参考

    1、Restricting an interface in C++ - Fluent C++ (fluentcpp.com)

    2、The quest of private inheritance in C++ | Sandor Dargo's Blog

  • 相关阅读:
    MySQL(DQL部分)
    Java函数式接口
    Java简易版生产者消费者模型
    浅谈Java三种实现线程同步的方法
    Linux常用命令
    计算机组成原理笔记
    资煌麻辣烫——事后诸葛亮
    资煌麻辣烫——冲刺集合
    资煌麻辣烫——测试总结
    资煌麻辣烫——冲刺总结
  • 原文地址:https://www.cnblogs.com/smartNeo/p/14806694.html
Copyright © 2011-2022 走看看