【问题标题】:Privatising a public method in a subclass of a base class将基类的子类中的公共方法私有化
【发布时间】:2020-09-11 21:58:51
【问题描述】:

我正在为图形研究一个类层次结构,其中我有这个方法称为disjoint_union。因为基类包含所有子类需要更新的属性(比如邻接表),所以我在这个类中放了一个方法,参数为graph,如下所示:

class graph {
    public:
        // ....
        void disjoint_union(const graph& g);
}

它的两个子类是ugraph(无向图)和dgraph(有向图)。 dgraph 类需要更新“in-adjacency”列表,即图的每个顶点的邻接列表。所以,我可以将graph::disjoint_union 方法声明为虚拟方法,这就是故事的结尾。但我不希望此类具有相同的方法(即具有相同的参数类型),因为它会接受无向图。所以,我的“解决方案”(并不是说它真的是一个问题)是:

class dgraph : public graph {
    public:
        // ....
        void disjoint_union(const dgraph& g);
        // ....
    private:
        using graph::disjoint_union;
}

如果我这样做:

dgraph dG;
// initialise dG
ugraph uG;
// initialise uG
dG.disjoint_union(uG);

编译器发出错误,因为在类dgraph 中唯一可以接受ugraph 的称为disjoint_union 的方法被声明为私有。这需要将方法声明为私有只是一种解决方法,因为我可以执行以下操作:

graph* dG = new dgraph(); // I know smart pointers should be chosen over raw pointers
// initialise dG
ugraph uG;
// initialise uG
dG.disjoint_union(uG);

一些可以被视为“坏”的东西,并且对于像有根树这样的子类会进一步恶化:

class rtree : public dgraph {
    public:
        // ....
        void disjoint_union(const rtree& t);
        // ....
    private:
        using dgraph::disjoint_union;
}

在执行此类操作后必须更新自己的属性。显然,rtree::disjoint_union 的可能实现是:

void rtree::disjoint_union(const rtree& t) {
    dgraph::disjoint_union(t);
    // update rtree's attributes
}

(在其他类中也是如此)。现在,C++17 允许这样做:

class graph {
    public:
        // ....
        virtual void disjoint_union(const graph& g);
}
class dgraph : public graph {
    public:
        // ....
        void disjoint_union(const dgraph& g);
        // ....
    private:
        // this time, graph::disjoint_union is not declared as private
        // using graph::disjoint_union;
}

但会引发一些警告,包括

graph.hpp:113: warning: ‘virtual void graph::disjoint_union(const graph&)’ was hidden [-Woverloaded-virtual]
  113 |   virtual void disjoint_union(const graph& g);
      |                ^~~~~~~~~~~~~~
dgraph.hpp:145: warning: 'dgraph::disjoint_union' hides overloaded virtual function

问题:有没有更好的方法来重载方法disjoint_union? (比将方法设为私有更好)我担心这些警告告诉我无法保证某些事情,例如不调用适当的方法,或者这种重载方式允许调用父类的 disjoint_union 方法(其中我不想)。任何帮助将不胜感激。

【问题讨论】:

  • graph的每个子类都需要实现disjoint_union吗?
  • void dgraph::disjoint_union(const dgraph&); 的参数与基类不同,因此隐藏了基类版本。我们仍然可以使用dG.graph::disjoint_union(uG); 调用基类实现。
  • 好吧,所以私有化似乎是要走的路……@FantasticMrFox,并不是每个子类都需要重新实现。但他们中的大多数。
  • 如果您想在ugraphdgraph 之间禁止disjoint_union,那么该方法可能不应该在基类中。
  • 好的,我把方法放在基类中,以免重复代码。那么ugraph::disjoint_uniondgraph::disjoint_union(目前在graph::disjoint_union)共同的代码应该放在其他一些函数中。

标签: c++ inheritance overloading virtual-inheritance


【解决方案1】:

是的,您会收到此警告,因为签名 disjoint_unions 方法因参数类型而异。

解决此警告的最佳方法是在派生类中执行类似操作

void disjoint_union(const graph& g)
{
    auto* d = dynamic_cast<dgraph*>(&g); // cast to pointer to avoid exceptions
    if (!d) {
        graph::disjoint_union(g);
        return;
    }
    // YOUR CODE IS HERE
}

【讨论】:

  • 对,我曾经有这个,但就我而言,dynamic_cast 似乎很慢。我的意思是,最好有一个适当类型的参数。
  • 因此,您的解决方案是保留graph 类型并使用动态转换。如果图形的类型不合适,则会引发异常,因此指针会避免它。但我不想用ugraphdgraphdisjoint_union,所以例外应该没问题。
  • 好吧,如果你觉得 dynamic_cast 太慢,那么你可以使用 SFINAE。
  • 我喜欢你的想法。 SFINAE 似乎非常合适,虽然我在忘记考虑在这里使用它之前使用过它。但我越想你的初始解决方案,我就越喜欢它。我将发布我自己的问题的答案,总结我的建议。我会提到您的帖子和其他用户的 cmets。虽然,由于您的答案是唯一的答案,但我会通过接受它来感谢您。谢谢。
猜你喜欢
  • 2011-12-08
  • 2016-08-08
  • 2014-02-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 2014-01-03
  • 1970-01-01
  • 2014-08-06
相关资源
最近更新 更多