简体   繁体   中英

Pass different classes as argument to a member function in c++

I would like to pass different classes as an argument to a function, but I am not sure how to do it in c++. The classes I want to pass have member functions with the same name. I wrote my program in JavaScript :

class Calculator {
    constructor(value) {
        this.value = value;
    }

    // here I want to pass in AddCommand and SubtractCommand class types
    executeCommand(command) {
        this.value = command.execute(this.value); // They both have .execute methods
    }
}

class AddCommand {
    constructor(valueToAdd) {
        this.valueToAdd = valueToAdd;
    }

    execute(currValue) {
        return currValue + this.valueToAdd;
    }

    undo(currValue) {
        return currValue - this.valueToAdd;
    }
}

class SubtractCommand {
    constructor(valueToSubtract) {
        this.valueToSubtract = valueToSubtract;
    }

    execute(currValue) {
        return currValue - this.valueToSubtract;
    }

    undo(currValue) {
        return currValue + this.valueToSubtract;
    }
}

As you can see my AddCommand and SubtractCommand classes both have execute member functions, and I want to pass instances of them to the executeCommand method in the Calculator class. How should I do it? I know the example might be trivial, but I am just asking how to deal with a case like this in general.

I can perform add and subtract like this:

let calculator = new Calculator(0);

calculator.executeCommand(new AddCommand(5));
console.log(calculator.value); // 5

calculator.executeCommand(new SubtractCommand(5));
console.log(calculator.value); // 0

Thank you for your help!

There's a few different to ways to tackle this problem depending on the specific needs of your program.

Here are the two techniques that are most commonly used:

1. Compile-time polymorphism

This is appropriate, and generally preferable, when the type of the command is known at the call site. It involves making executeCommand() a function template, ie a function that produces multiple implementations "on-demand" based on the arguments passed to it.

In modern (c++20) C++, you can use a concept to make the expected interface explicit. In older versions of the language, you would make the executeCommand() method accept any type and just use it as if the required methods are implemented. This is called duck-typing.

#include <concepts>

template<typename T>
concept Command = requires(const T& cmd, int v) {
  { cmd.execute(v) } -> std::convertible_to<int>;
  { cmd.undo(v) } -> std::convertible_to<int>; 
};

class Calculator {
   int value;
public:
    Calculator(int v) : value(v) {}

    template<Command CmdT>
    void executeCommand(const CmdT& command) {
        value = command.execute(value);
    }

    // For C++17 and earlier:
    // template<typename CmdT>
    // void executeCommand(const CmdT& command) {
    //     value = command.execute(value);
    // }

};

class AddCommand {
    int valueToAdd;
public:
    AddCommand(int v) : valueToAdd(v){}

    int execute(int currValue) const {
        return currValue + valueToAdd;
    }

    int undo(int currValue) const {
        return currValue - valueToAdd;
    }
};

int main() {
  Calculator calc{0};

  AddCommand command{3};

  calc.executeCommand(command);
}

2. Runtime Polymorphism with virtual methods.

This is appropriate if the actual type of the command is erased by the time it's used. In this scenario, you declare a base interface class that the various implementations inherit from. Invoking the methods on pointers and references to the base class will call the subclass's implementations.

class Command {
public:
  virtual ~Command() {}

  virtual int execute(int currValue) const = 0;
  virtual int undo(int currValue) const = 0;
};

class Calculator {
   int value;
public:
    Calculator(int v) : value(v) {}

    void executeCommand(const Command& command) {
        value = command.execute(value);
    }
};

class AddCommand final : public Command {
    int valueToAdd;
public:
    AddCommand(int v) : valueToAdd(v) {}

    int execute(int currValue) const override {
        return currValue + valueToAdd;
    }

    int undo(int currValue) const override {
        return currValue - valueToAdd;
    }
};

void process(const Command& cmd) {
  Calculator calc{0};
  calc.executeCommand(cmd);
}

int main() {
  AddCommand command{3};

  process(command);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM