【问题标题】:Is there a way to prevent calls to a specific inherited function in a derived class?有没有办法防止调用派生类中的特定继承函数?
【发布时间】:2015-11-11 22:09:02
【问题描述】:

考虑以下几点:

class base {

    // base class

public:
    void doBaseStuff() {
        /* do something basic */
    }
private:
    int someDataMember;
};

class derived : public base {

    // this class inherits 'doBaseStuff()' as well as 'someDataMember'

public:
    void doDerivedStuff() {
        /* do something in addition that only this type of derived class can do */
    }
};

class specialDerived : public base {

    // say 'doBaseStuff()' doesn't make sense in the context of this class!
    // we do not want to inherit that function here
    // is there a way to prevent instances of this class from calling it?
    // we still want 'someDataMember', so we must derive from the base class

public:
    void doSpecialStuff() {
        /* do something in addition that only this special derived class can do */
    }
};

有一个类可以作为多个其他类的公共基类。但是,假设它在派生类之一中不需要的基类成员函数之一。有没有办法明确禁止派生类中的继承函数?

实际上,我们可以谈论一个有 1000 个成员的基类,而我们想要继承其中的 999 个,因此简单地放弃继承并复制所有代码会导致代码的可维护性非常差。

在基类中使该函数成为虚拟函数,并在specialDerived 类中提供它的虚拟实现是可行的。但是,如果该函数有一个实际的返回类型,而不是void,该怎么办?然后specialDerived 类必须提供一个返回值的实现。然后调用者可以假设他们得到的值是有意义的,而实际上并非如此,这是一个问题。

只需在 specialDerived 中使用单个 throw 在主体中实现虚函数可能是一种解决方案,只要存在一些明确指出在此特定派生类型的对象上调用此函数没有意义的文档,并且这个操作会抛出。我认为这是一种可能的解决方案,但我不确定这是否是最好的方法。

【问题讨论】:

  • 您不能阻止对您继承的非虚函数的调用。就此而言,您不能阻止调用公共成员函数的基类实现。但是你可以简单地避免继承。
  • 继承是is-a 关系,如果它的一个(或多个)操作对您没有意义,显然这不是is-a 关系,所以您的问题是您不应该首先继承。
  • @adam10603 第一时间实施正确的接口!无论您使用的是动态(纯虚函数)还是静态(查找 CRTP)多态性。事实上,您的设计存在严重缺陷。
  • @adam10603 如果您无法返回合理的值,请改为抛出异常。一般来说,我们无法判断这是否是错误行为,这完全取决于您的实际用例。
  • 抛出异常似乎是一个合适的回应。

标签: c++ class inheritance member virtual-functions


【解决方案1】:

我假设您只是想确保该方法不会被意外调用,即调用时的编译错误是完美的。

您可以简单地用删除的定义覆盖specialDerived 中的doBaseStuff

class specialDerived : public base {
  void doBaseStuff() = delete;

public:
  void doSpecialStuff() {
      /* do something in addition that only this special derived class can do */
  }
};

这将阻止任何人使用specialDerived::doBaseStuff。仍然可以将对specialDerived 的引用转换为对base 的引用并在结果上调用doBaseStuff,但是如果不创建doBaseStuff virtual,就无法避免这种情况。

【讨论】:

  • 在我的情况下,用 throw 覆盖一个虚函数更有意义,但我仍然接受这个作为答案,因为我并没有真正提供关于我的具体情况。我的问题非常笼统,这确实回答了它。然而,就我而言,只要发生这种情况,我就会扔掉。不过还是谢谢!其他任何人都可以发现这很有用,因为它确实有效,所以我把这个作为答案留在这里。
【解决方案2】:
  1. base 中声明doBaseStuff 受保护
  2. base 派生一个新类 base2,这只会使 doBaseStuff 公开
  3. base2而不是base派生999个类
  4. base 派生一个类

像这样:

class base {
protected:
    void doBaseStuff() {
        /* do something basic */
    }
private:
    int someDataMember;
};

class base2 : public base {
public:
    using base::doBaseStuff; // make it public
};

class derived : public base2 {
   // ...
};

class specialDerived : public base {
   // doBaseStuff not public here since we're derived only from base (and not base2) and didn't make it public ourself
};

【讨论】:

  • 没有必要在base 中保护doBaseStuff()。在大多数情况下,可以简单地将其完全移至base2()
  • 有时可以,有时不能 :) 我想展示一个不那么简单的版本,其中一个不能,看起来如何。 (我个人也经常遇到这种不那么琐碎的版本。)
猜你喜欢
  • 2014-03-22
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 2016-10-08
  • 2011-01-08
  • 2018-03-26
  • 1970-01-01
  • 2011-01-09
相关资源
最近更新 更多