【问题标题】:What detectable differences are there between a class and its base-class?一个类和它的基类之间有什么可检测的差异?
【发布时间】:2010-08-25 04:39:38
【问题描述】:

给定以下模板:

template <typename T>
class wrapper : public T {};

Foo 类型的对象和wrapper&lt;Foo&gt; 类型的对象在接口或行为上有哪些明显的区别?

我已经知道一个:

  • wrapper&lt;Foo&gt; 仅具有空值构造函数、复制构造函数和赋值运算符(仅当这些操作在 Foo 上有效时才具有)。这种差异可以通过在 wrapper&lt;T&gt; 中使用一组模板化构造函数来缓解,这些构造函数将值传递给 T 构造函数。

但我不确定可能存在哪些其他可检测的差异,或者是否有隐藏它们的方法。


(编辑)具体示例

有些人似乎在询问这个问题的背景,所以这里是对我的情况的(稍微简化的)解释。

我经常编写具有可调整值的代码,以调整系统的精确性能和操作。我希望有一种简单(低代码开销)的方式来通过配置文件或用户界面公开这些值。我目前正在编写一个库来允许我这样做。预期的设计允许这样使用:

class ComplexDataProcessor {
    hotvar<int> epochs;
    hotvar<double> learning_rate;
public:
    ComplexDataProcessor():
        epochs("Epochs", 50),
        learning_rate("LearningRate", 0.01)
        {}

    void process_some_data(const Data& data) {
        int n = *epochs;
        double alpha = *learning_rate;
        for (int i = 0; i < n; ++i) {
            // learn some things from the data, with learning rate alpha
        }
    }
};

void two_learners(const DataSource& source) {
    hotobject<ComplexDataProcessor> a("FastLearner");
    hotobject<ComplexDataProcessor> b("SlowLearner");
    while (source.has_data()) {
        a.process_some_data(source.row());
        b.process_some_data(source.row());
        source.next_row();
    }
}

运行时,这将设置或读取以下配置值:

FastLearner.Epochs
FastLearner.LearningRate
SlowLearner.Epochs
SlowLearner.LearningRate

这是由代码组成的(碰巧我的用例甚至不是机器学习),但它显示了设计的几个重要方面。可调整的值都是命名的,并且可以组织成层次结构。值可以通过几种方法进行分组,但在上面的示例中,我只展示了一种方法:将对象包装在 hotobject&lt;T&gt; 类中。在实践中,hotobject&lt;T&gt; 包装器有一个相当简单的工作——它必须将对象/组名称推送到线程本地上下文堆栈,然后允许构造 T 对象(此时 hotvar&lt;T&gt;构造值并检查上下文堆栈以查看它们应该在哪个组中),然后弹出上下文堆栈。

按如下方式完成:

struct hotobject_stack_helper {
    hotobject_stack_helper(const char* name) {
        // push onto the thread-local context stack
    }
};

template <typename T>
struct hotobject : private hotobject_stack_helper, public T {
    hotobject(const char* name):
        hotobject_stack_helper(name) {
        // pop from the context stack
    }
};

据我所知,这个场景中的构造顺序非常明确:

  1. 构造hotobject_stack_helper(将名称推入上下文堆栈)
  2. T 被构造——包括构造T 的每个成员(热变量)
  3. hotobject&lt;T&gt; 构造函数的主体运行,弹出上下文堆栈。

所以,我有工作代码来执行此操作。然而,还有一个问题,那就是:通过使用这种结构,我可能会给自己带来什么问题。这个问题很大程度上归结为我实际要问的问题:hotobject 的行为与 T 本身有何不同?

【问题讨论】:

  • 所以,您正在尝试制作一个完全不执行任何操作的包装模板...可能有人问为什么?
  • 只有当类中有公共方法时才存在“可见”行为。
  • 真正的包装器模板确实有行为(虽然没有额外的成员),当然,但这种额外行为的效果是显而易见的。该问题旨在隔离最小情况下存在的差异(这是一个什么都不做的包装类)。
  • 我应该指出,很明显,我永远无法完全隐藏hotobject&lt;T&gt;T 之间的区别。但是,一些差异可能是可以接受的(因为我可以轻松解决它们,或者因为它们根本不会出现在我的特定用例中),而其他差异可能代表一个显示停止问题,这意味着我必须重新设计库才能使用不同的结构。

标签: c++ templates mixins


【解决方案1】:

奇怪的问题,因为你应该问关于你的具体用法的问题(“我想做什么,这对我有什么帮助或伤害”),但我猜一般:

wrapper&lt;T&gt; 不是T,所以:

  • 它不能像T 那样构造。 (如您所见。)
  • 不能像T 那样转换。
  • 它无法访问 T 可以访问的私有数据。

我敢肯定还有更多,但前两个涵盖了很多。

【讨论】:

    【解决方案2】:

    假设你有:

    class Base {};
    class Derived : Base {};
    

    现在你可以说:

    Base *basePtr = new Derived;
    

    但是,你不能说:

    wrapper<Base> *basePtr = new wrapper<Derived>();
    

    也就是说,即使它们的类型参数可能有继承关系,但是通过特化模板产生的两种类型没有任何继承关系。

    【讨论】:

      【解决方案3】:

      对对象的引用可转换(授予访问权限)为对基类子对象的引用。有语法糖来调用隐式转换,允许您将对象视为基的实例,但这确实是正在发生的事情。不多也不少。

      因此,根本不难发现差异。它们(几乎)是完全不同的东西。 “is-a”关系和“has-a”关系之间的区别在于指定成员名称。

      至于隐藏基类,我认为您无意中回答了自己的问题。通过指定private(或为class 省略public)来使用私有继承,并且这些转换不会发生在类本身之外,并且没有其他类能够判断基类甚至存在。

      【讨论】:

      • 我不是想隐藏基类,我是想让 wrapper 看起来尽可能像 T (我现在添加了我在问题)
      • 我明白了。好吧,就引用和指针可隐式转换以及对基的成员的访问而言,它看起来像基。你已经知道了。但除此之外,它们会产生不同的模板专业化等。目前尚不清楚您要消除什么样的差异。我给出的简单答案是它们完全不同。您似乎关心的是兼容性,然后我们需要知道它们使用什么样的接口。
      • 是的,这很公平——我想我可能只需要吸吮它就可以了。
      【解决方案4】:

      如果你继承的类有自己的成员变量(或至少一个),那么

      sizeof(InheritedClass) > sizeof(BaseClass)
      

      【讨论】:

      • struct BaseClass {}; struct InheritedClass : BaseClass {}; static_assert(sizeof(BaseClass) == sizeof(InheritedClass), ":P");
      • 技术上,非静态成员变量。无论如何,sizeof 并没有提供关于类层次结构的大量信息。
      • @Potatoswatter : true @GMan : 我清楚地说明了继承类应该有成员变量(静态;-))的假设,以使我的断言为真
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-11
      • 1970-01-01
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多