代码之家  ›  专栏  ›  技术社区  ›  Imbue

C++隐式转换运算符

  •  6
  • Imbue  · 技术社区  · 15 年前

    我想在C++中找到一个好的继承解决方案。

    我有一个矩形班和一个正方形班。Square类不能从Rectangle公开继承,因为它不能完全满足矩形的要求。例如,一个矩形可以有它的宽度和高度每一个单独设置,这当然是不可能与一个正方形。

    所以,我的困境。Square显然会与矩形共享许多代码;它们非常相似。

    例如,如果我有如下功能:

    bool IsPointInRectangle(const Rectangle& rect);
    

    它也适用于正方形。实际上,我有很多这样的功能。

    因此,在生成Square类时,我考虑使用私有继承和一个公共可访问的矩形转换运算符。所以我的方形课看起来像:

    class Square : private Rectangle
    {
        public:
            operator const Rectangle&() const;
    };
    

    但是,当我试图将一个正方形传递给ispointinRectangle函数时,编译器会抱怨在该上下文中“矩形是不可访问的基”。我希望它注意到矩形操作符并使用它。

    我想做的是可能的吗?

    如果这不起作用,我可能要将矩形的一部分重构为 MutableRectangle 班级。

    谢谢。

    3 回复  |  直到 15 年前
        1
  •  3
  •   Shiroko    15 年前

    嗯,我很惊讶。似乎私下继承类A会阻止您在类之外使用运算符A。

    可以通过将成员矩形设为正方形并将其用于强制转换来解决问题:

    class Square {
        Rectangle r;
        public:
            operator const Rectangle&() const {
                return r;
            }
    };
    

    这应该编译并工作。我相信这不会给你更多的工作要做,如果有的话。

        2
  •  6
  •   Alex Martelli    15 年前

    你可以去上课 ImmutableRectangle ,没有任何突变,只有 const 方法,从中可以正确地派生 Rectangle ,分别, ImmutableSquare 从那以后, Square . 注意,如果不考虑可变性, IS-A 关系 hold——一个不可变的正方形是一个不可变的矩形:可变性是唯一严重的问题,所以通过分解它,您可以得到一些实质性的代码重用(对于所有的 康斯特 用途——实际上不使用或不需要可变性的用途)。

    只要(不变的)基没有类不变量,沿着继承引入可变性就可以了。 依靠 关于不可变特性;当然,不可变对象可以用 康斯特 指向可变版本的指针或引用(可能在单独的内联友元函数中,以避免为基类提供对派生类的依赖;-)以便合理方便地使用。

    编辑 :一条注释可以理解地表达了疑虑,因为“mutabe不是不可变的”:为此,您需要了解“is-a”是什么。 方法 …确实如此 意思是 Korzybski -拒绝“ is “身份”的意思是 LSP . 这意味着:协方差、反方差、弱等预条件、强等后条件等。 因为它们适用于 康斯特 方法 基本类(不可变)和派生类(可变)的。您将看到类不变量是唯一的问题,正如我在前一段中提到的,所以只需避免断言不可变为类不变量,您就可以使用clover;-)。

    也许这有助于命名基类 NotNecessarilyMutableRectangle 因为它没有 断言 作为类不变量的不变性;这种非常精确的命名在哲学上可能令人放心,但在日常编码中可能有点不方便。

        3
  •  0
  •   Jon Purdy    15 年前

    我相信,尽管我不确定,您必须使用显式强制转换在该上下文中调用该转换运算符。这个 ImmutableRectangle 碱是一种常见而有效的解决方案。类似地,您可以使用更抽象的解决方案,例如:

    /**
     * Base for rectangular classes; name it whatever you want.
     */
    class RectangularBase {
    public:
    
        virtual unsigned int getValue(int) const = 0;
    
    };
    
    /**
     * Base for specific rectangular classes; also named whatever.
     */
    template<unsigned int Dimensions>
    class Rectangular : public RectangularBase {
    public:
    
        virtual unsigned int getValue(int index) const { return values[index]; }
    
    private:
    
        unsigned int values[Dimensions];
    
    };
    
    /**
     * A square is Rectangular but has only one significant dimension.
     */
    class Square : public Rectangular<1> {
    public:
    
        unsigned int getSideLength() const { return getValue(0); }
    
    };
    
    /**
     * A Rectangle is Rectangular and has two significant dimensions.
     */
    class Rectangle : public Rectangular<2> {
    public:
    
        unsigned int getWidth() const { return getValue(0); }
        unsigned int getHeight() const { return getValue(1); }
    
    };