C++ 等价于 Java 的匿名 class

C++ equivalence to Java's anonymous class

我正在努力将一些 Java 代码翻译成 C++。

在 Java 中,我们可以从匿名 class 创建对象,使用现有的构造函数,并覆盖一些方法。例如,

class X {
    public X(int value) {...}
    public void work() {....}
} 

void main(String[] args) {
    X entity = new X(5) {
         public void work() { /* Something else */ }
    };
}

在 C++ 中,我知道我可以创建匿名 class,如下所示:

class X {
public:
    virtual void work() {...}
}

class : public X {
public:
    void work() {....}
} obj;

但 C++ 不允许在匿名 class 中构造函数,并且它不允许从对象扩展(例如,像 Java 允许的那样 new X(5) { public void work() {} }

如何用 C++ 编写类似的代码?

更新 03/07/2020 05:27 CDT

关于我正在处理的问题的更多背景信息。我正在实现内存SQL数据库的聚合函数,并使用以下class表示聚合字段:

class AggField {
public:
    AggField(int colIndex);
    virtual void reduce(DataRow&) = 0;
    virtual double output() = 0;
}

对于每种类型的聚合,例如 avg、min/max 和 sum,我都有一个 subclass。例如

class Avg : public AggField {
private:
    int counter_;
    double value_;
public:
    Avg(int colIndex) : AggField(colIndex), counter_(0), value_(0) {};
    void reduce(DataRow&) override {
        value_ += row[colIndex].doubleval();
        counter_ += 1;
    }
    double output() override {
        return value_ / counter_;
    }
}

class Sum : public AggField {
    .....
}

在处理一个table的时候,我会这样写

Table table = ...
auto agg_opr = Agg({
    new Sum(0),
    new Avg(1)
});
agg_opr.agg(table);

对第 0 列求和,对第 1 列取平均值。

有时(罕见)我需要处理多个输入列。例如,对 col1 * (1 + col2) 求和。我不想创建 AggField 的新子class,而是想写一些类似于:

Table table = ...
auto agg_opr = Agg({
    new Sum(0) {
        void reduce(DataRow& row) {
             value_ += row[0].doubleval() * (1 + row[1].doubleval());
        }
    }, 
    new Avg(1),
    new Max(1)
});
agg_opr.agg(table);

C++ 具有匿名名称空间,这可以有效地让您创建 classes,它们与它们在其中声明的翻译单元完全隔离:

namespace {

   class X {

   public:
          X(int) { /* ... */ }     // Ok to have a constructor

          void work();
    };

}

int main(int argc, char **argv)
{
    X entity{5};

    // ...
}

现在,您必须在全局范围内声明它们,不能在内部范围内声明它们。您还需要为这些 classes 提供您可以在同一翻译单元中引用它们的正常名称;但出于所有实际目的,它们是完全匿名的,其他翻译单位无法访问。另一个翻译单元可以声明自己的匿名class"X",不会有任何冲突。

您可以在所有其他正常方式中使用匿名 classes,subclass 它们,等等...您可以创建一个匿名 class 这是一个 subclass 的常规、非匿名 class,这让你非常接近 Java 所做的,在这里。

一些编译器还提供扩展,您可以在其中 在内部范围内声明 classes,它们也可以工作与匿名名称空间非常相似,但这将是特定于编译器的扩展。

我不能说我知道如何编写地道的 Java 但我猜测 Java 中的这种模式是 C++ 中 lambda 的替代方法。我记得很久以前在使用 Swing 时使用匿名 class。我想我做了这样的事情:

button.addMouseListener(new MouseAdapter() {
  public void mouseClicked(MouseEvent e) {
    // ...
  }
});

这是从 class 继承并覆盖方法的糖。正是这样做并不是我想要在 C++ 中附加事件侦听器的方式。我更愿意这样做:

button.addMouseClickListener([](const MouseEvent &e) {
  // ...
});

在事件侦听器的情况下,闭包需要存储在 std::function 或类似的东西中。这与虚拟调用的性能大致相同。

我真的不太了解你在哪里使用这个 class 但是如果你需要把它放在一边(比如事件监听器之类的)然后声明 class long方式或使用 std::function 可能是最干净的选择。如果你不需要把它放在一边(比如算法的策略),那么你可以使用仿函数。当然,您可以将仿函数放在一边,但它需要一些模板机制,而且可能不值得(尽管它确实具有更大的灵活性)。

struct MyPolicy {
  int doSomething(int i) {
    return i * 3;
  }

  double getSomething() const {
    return d;
  }

  double d;
};

template <typename Policy>
void algorithm(Policy policy) {
  // use policy.doSomething and policy.getSomething...
}

在模板中使用仿函数或 lambda 比使用虚函数具有更好的性能。在上面的示例中,编译器可以并且可能会内联对 doSomethinggetSomething 的调用。这对于虚函数是不可能的。

如果我对您要解决的实际问题了解得更多,那么我可能会写出更具体、更有帮助的答案。


看到更新后的问题后,我有另一个建议。那就是为自定义聚合函数创建一个 subclass。当然,这有很多限制。

template <typename Func>
class CustomAgg : public AggField {
public:
    CustomAgg(int colIndex, Func func)
      : AggField{colIndex}, func{func} {}

    void reduce(DataRow &row) override {
        func(value, row);
    }

    double output() override {
        return value;
    }

private:
    Func func;
    double value = 0.0;
    // could add a `count` member if you want
};

auto agg_opr = Agg({
    new CustomAgg{0, [](double &value, DataRow &row) {
        value += row[0].doubleval() * (1 + row[1].doubleval());
    }},
    new Avg(1),
    new Max(1)
});

老实说,我认为最适合您的解决方案是不要尝试在 C++ 中实现 Java 功能。我的意思是,如果您需要在某些特定操作中处理多个列,那么只需为此创建一个 class。不要走任何捷径。给它一个名字,即使你可能只在一个地方使用它。