C++中等价于Java匿名类的概念

3

我正在翻译一些Java代码到C++。

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

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 X {
public:
    virtual void work() {...}
}

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

但是C++不允许在匿名类中使用构造函数,并且不允许从对象继承(例如,像Java允许的new X(5) { public void work() {} })。

我该如何在C++中编写类似的代码?

更新 03/07/2020 05:27 CDT

关于我正在解决的问题的更多上下文。我正在实现内存中SQL数据库的聚合函数,并使用以下类表示聚合字段:

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

对于每种聚合类型,例如平均值、最小/最大值和总和,我都有一个子类。例如:

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 = ...
auto agg_opr = Agg({
    new Sum(0),
    new Avg(1)
});
agg_opr.agg(table);

这段代码会对第0列求和,对第1列求平均。

有时候(很少)我需要处理多个输入列。例如,对col1 * (1 + col2)进行求和。我不想创建一个新的AggField子类,而是希望编写类似于以下内容的代码:

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);

3
你需要给这个班取一个名字。 - Raymond Chen
我猜你可以使用函数指针来实现类似的效果,而不需要一个全新的类型。(如果你不能改变 X 或需要添加到该类型,则这不是正确的方法)class X { std::function<void()> work = [] () { /***/ }; }; int main() { X foo { [] () { /***/ } }; }. - George
2个回答

3

我不敢说我知道如何编写符合惯用法的Java代码,但我猜想Java中的这种模式是C++中Lambda表达式的另一种选择。我记得很久以前在使用Swing时曾经使用过匿名类。我认为当时我的做法是这样的:

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

这是继承类并覆盖方法的简单写法。但我并不推荐在 C++ 中使用这种方式来添加事件监听器。我更喜欢这样做:
button.addMouseClickListener([](const MouseEvent &e) {
  // ...
});

在事件监听器的情况下,闭包需要存储在std::function或类似的对象中。这与虚函数调用大致相同。
我不太清楚您在哪里使用此类,但如果您需要将其存储在一旁(例如事件监听器等),那么按照传统的方式声明该类或者使用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。这对于虚函数来说是不可能的。
如果我了解更多关于您正在尝试解决的实际问题,那么我可能能够写出更具体和有用的答案。
看到更新后的问题后,我有另一个建议。那就是为自定义聚合函数创建一个子类。当然,这有许多限制。
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功能。我的意思是,如果您需要在某些特定操作中处理多个列,则只需创建一个专门用于此目的的类。不要走捷径。即使您可能只在一个地方使用它,也要给它起个名字。


感谢您的回答和建议。我正在编写一个具有多个方法的聚合,因此单个函数指针无法解决问题。我已经更新了我的问题,以更精确地描述上下文。 - Harper
我喜欢创建一个单独的子类来定义自定义动作和函数的想法。 - Harper

0

C++有匿名命名空间,这有效地让你创建完全隔离于它们声明的翻译单元的类:

namespace {

   class X {

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

          void work();
    };

}

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

    // ...
}

现在,你必须在全局范围内声明它们,不能在内部范围内声明。你还需要给这些类普通的名称,以便在同一翻译单元中引用它们;但是对于所有实际目的来说,它们完全是匿名的,并且无法从其他翻译单元访问。另一个翻译单元可以声明它自己的匿名类"X",并且不会有任何冲突。

你可以以所有其他正常方式使用匿名类,对它们进行子类化等等... 你可以创建一个匿名类,它是一个普通的、非匿名类的子类,这使你非常接近Java所做的事情。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接