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 比使用虚函数具有更好的性能。在上面的示例中,编译器可以并且可能会内联对 doSomething
和 getSomething
的调用。这对于虚函数是不可能的。
如果我对您要解决的实际问题了解得更多,那么我可能会写出更具体、更有帮助的答案。
看到更新后的问题后,我有另一个建议。那就是为自定义聚合函数创建一个 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。不要走任何捷径。给它一个名字,即使你可能只在一个地方使用它。
我正在努力将一些 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 比使用虚函数具有更好的性能。在上面的示例中,编译器可以并且可能会内联对 doSomething
和 getSomething
的调用。这对于虚函数是不可能的。
如果我对您要解决的实际问题了解得更多,那么我可能会写出更具体、更有帮助的答案。
看到更新后的问题后,我有另一个建议。那就是为自定义聚合函数创建一个 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。不要走任何捷径。给它一个名字,即使你可能只在一个地方使用它。