【问题标题】:Adding publicly but inheriting private公开添加但继承私有
【发布时间】:2009-11-06 12:37:09
【问题描述】:

我想通过避免公共继承将一个类直接添加到一个新类中。例如我有一个像

这样的类
class size {
private:
    int width;
    int height;
public:
    void set_width(int w) { width = w; }
    int get_width() { return width; }
    void set_height(int h) { height = h; }
    int get_height() { return height; }
    int get_area() { return width*height; }
};

只是想将它的功能插入到这样的新类中

class square : public size { 
    // ...
};

会写

square s;
s.set_width(10);
s.set_height(20);
cout << "Area of the square: " << s.get_area() << endl;

但是这样我就违反了公共继承的 is-a 规则。我的square 不是sizehas-a size。所以我必须写

class square {
public:
    size its_size;
    // ...
};

但是现在我最初将size 的功能插入square 的想法丢失了。我要写

square s;
s.its_size.set_width(10);
s.its_size.set_height(20);
cout << "Area of the square: " << s.its_size.get_area() << endl;

或为size 的getter 和setter 添加几个包装器到square

编辑: 我必须补充:size 注定不会有虚拟析构函数。我不想使用size,它是多态的后代。

编辑 2: 另一个示例:您想编写一个类,它提供与 std::list&lt;T&gt; 相同的接口,但提供的功能比简单的独立函数所能完成的要多得多。标准容器不应被子类化,因此您必须添加 std::list&lt;T&gt; 作为成员并将所有公开提供的 std::list&lt;T&gt; 函数直接包装到您的新类中。这是很多重复性的工作,而且容易出错。

问题: 是否有可能将size 的接口公开加入 square 而不公开继承size。我的square 不应该是size,但应该提供相同的界面(在它自己的部分旁边)。

【问题讨论】:

    标签: c++ class


    【解决方案1】:

    为什么你的界面是size?为什么不将其设为shapeboundedarea 或类似的名称,它们也描述了界面,并且对于其中任何一个,正方形肯定is-a(形状/边界区域)?

    【讨论】:

    • 是的,我认为这是一个命名问题。除非你想在别处使用尺码等级
    • 不,这只是一个例子。我不想多态地使用大小。 size 的功能应该简单地添加到 square 中。
    • 也请考虑我的最新编辑。如果我想将 std::list 插入我的班级会发生什么?
    • 那么您可能应该将其用作成员变量以允许进行适当的封装 - 虽然 std::list 不太可能改变,如果您想使用不同的基础对象,或者如果不知何故它确实改变了,你不会想修改任何东西,除了你的一个类来进行改变。
    【解决方案2】:

    不,没有这种可能性。如果你想重用一个“接口”(这个名字在 C++ 中没有正式存在),你必须使用继承。

    但在这种情况下,我不认为继承是一个坏主意。

    编辑:

    从 std:: 容器继承没有问题。虽然对我来说没有意义,但以下代码在 Visual Studio 2005 上编译:

    #include <list>
    
    class myclass : public std::list<int>
    {
    };
    

    【讨论】:

    • 如果我想将 std::list 插入我的班级,这是完全被禁止的。标准容器不应该是子类
    • 这是合法的,但不是有意的,因为它们没有虚拟析构函数。指向基类的指针不会正确删除。
    【解决方案3】:

    听起来你想告诉全世界你的square 实现了一个接口,即size 接口。

    通常,C++ 中的接口表示为抽象基类(不幸的是,它无法绕过 is-a)。您可以在此处解决此问题的一种方法是使用duck typing。在这种情况下,实现sizesquare 上提供的函数并将调用转发到包含的size 对象。除非您的代码要求可以将对象转换为 size 类型的对象,而不是只要求存在函数,否则这应该可以正常工作。

    【讨论】:

    • 没错。但是如果我要转发大量的公共功能,我该怎么办?或者,如果我想将 size 插入到许多类中。
    • 在这种情况下,我认为务实的答案是直接从size 继承,或者从为您进行转发并包含大小本身的基类继承。是的,它可能违背了面向对象的原则,但如果你不这样做,你最终会陷入复制/粘贴地狱,并尝试在每个类中复制转发,尤其是对于大量函数。
    【解决方案4】:

    正方形有一个大小作为它的组成部分,我不认为你通过继承违反了任何规则。只要这些组件的行为保持相同(相同的宽度、高度、面积),否则您可能希望在基类中将它们声明为虚拟,以便在必要时能够覆盖它们。

    正如Dav 所说,如果这个名字对你来说真的很奇怪,你可以找到一个更好的命名约定,但就功能而言,这就是你想要的继承,为什么要让代码更难阅读和用吗?

    您也可以检查此检查 this documentation on inheritance 以用类似的示例说服您。

    【讨论】:

      【解决方案5】:

      听起来您需要一个名为“sizeable”的“界面”。在那里,您可以定义您希望 square 和其他相当大的对象实现的那些方法。由于计算会根据形状类型而有所不同,我认为已经使用实现定义了 size 对象是没有意义的。

      如果您想通过方法植入来保持对象的大小,也可以按照您在原始帖子中的说明使用组合。您仍然可能希望将 size 对象更改为 square_size,类似这样。命名法也适用于 has-a 关系。

      【讨论】:

        【解决方案6】:

        为什么不编写你的课程?让square 有一个size ivar,当你在square 类上调用getArea() 时,只需让它在内部调用its_size.getArea() 并返回该值。

        【讨论】:

        • 因为size很简单,只转发5个函数就可以了。但是如果我必须转发 15 个函数会发生什么?或者如果我想将大小插入到许多对象中?
        【解决方案7】:

        这里使用继承违反了里氏替换原则,所以我会使用组合。

        为了访问组合类,如果类很小(只有几个函数,如size),我会为其成员函数定义包装器,或者如果班级很大。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-05
          • 1970-01-01
          • 2011-03-11
          • 1970-01-01
          • 2015-07-14
          • 1970-01-01
          • 2011-08-10
          相关资源
          最近更新 更多