C ++ эквивалентность анонимному классу Java - PullRequest
0 голосов
/ 07 марта 2020

Я работаю над переводом некоторого 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 ++ не допускает конструктор в анонимном классе и не допускает расширение от объект (например, new X(5) { public void work() {} } как то, что позволяет Java.

Как я могу написать подобный код на C ++?

Обновление 03/07/2020 05:27 CDT

Дополнительные сведения о проблеме, над которой я работаю. Я реализую функцию агрегирования базы данных SQL в памяти и использую следующий класс для представления поля агрегации:

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

Для каждого типа агрегации, например, avg, min / max и sum, у меня есть подкласс. Например

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

Ответы [ 2 ]

2 голосов
/ 07 марта 2020

Не могу сказать, что знаю, как написать идиомати c Java, но я предполагаю, что этот шаблон в Java является альтернативой лямбда-выражениям в C ++. Я помню, как использовал анонимный класс длиной go, когда работал с 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...
}

Использование функтора или лямбды с шаблоном имеет гораздо лучшую производительность, чем использование виртуальных функций. В приведенном выше примере компилятор может и, вероятно, встроит вызовы doSomething и getSomething. Это невозможно с виртуальными функциями.

Если бы я знал больше о реальной проблеме, которую вы пытаетесь решить, я мог бы написать более конкретный c и полезный ответ.


После просмотра обновленного вопроса у меня есть еще одно предложение. Это было бы для создания подкласса для пользовательских агрегатных функций. Конечно, это имеет множество ограничений.

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

Честно говоря, я думаю, что лучшее решение для вас - не пытаться реализовать функцию Java в C ++. Я имею в виду, если вам нужно обрабатывать несколько столбцов в какой-то конкретной операции c, то создайте класс только для этого. Не делайте ярлыков. Дайте ему имя, хотя вы можете использовать его только в одном месте.

0 голосов
/ 07 марта 2020

C ++ имеет анонимные пространства имен, что позволяет эффективно создавать классы, полностью изолированные от единиц перевода, в которых они объявлены:

namespace {

   class X {

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

          void work();
    };

}

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

    // ...
}

Теперь вам нужно объявить их в глобальной области видимости. не объявляйте их во внутреннем объеме. Вам также нужно будет дать этим классам нормальные имена, по которым вы можете ссылаться на них в одной и той же единице перевода; но для практических целей они полностью анонимны и недоступны из других переводческих единиц. Другой модуль перевода может объявить свой собственный анонимный класс "X", и никаких конфликтов не будет.

Вы можете использовать анонимные классы всеми другими обычными способами, подкласс их и т. Д. c ... Вы может создать анонимный класс, который является подклассом обычного неанонимного класса, что довольно близко познакомит вас с тем, что делает Java.

Некоторые компиляторы также предлагают расширения, в которых вы может объявлять классы во внутренних областях, и они также будут работать очень похоже на анонимные пространства имен, но это будет расширение c, определяемое компилятором.

...