【问题标题】:How to create an inline implementation of an abstract class? [duplicate]如何创建抽象类的内联实现? [复制]
【发布时间】:2016-05-10 19:03:38
【问题描述】:

假设我有以下回调类:

class LogCallback {
public:
    virtual void sendLog(std::string log) = 0;
    virtual void setErrorCode(int code) = 0;
};

我有接受回调实现的引擎:

class Engine {
public:
    Engine();
    virtual ~Engine();
    void setCallback(LogCallback* callback);
    void start();
private:
    LogCallback* logCallback;
};

现在我可以创建一个实现类了:

class OutLogger : public LogCallback {

    void sendLog(std::string log) {
        cout << "out: " << log << endl;
    }

    void setErrorCode(int code) {
        sendLog("error code " + std::to_string(code));
    }

};

并使用它:

int process() {
    Engine engine;
    OutLogger out;
    engine.setCallback(&out);
    engine.start();
}

在 C++11 中,我还可以使用匿名本地类:

int anonymous() {
    Engine engine;
    class : public LogCallback {

        void sendLog(std::string log) {
            cerr << "err: " << log << endl;
        }

        void setErrorCode(int code) {
            sendLog("error code " + std::to_string(code));
        }

    } err;
    engine.setCallback(&err);
    engine.start();
}

现在有一个问题:我可以在没有显式匿名类的情况下在函数调用中做同样的事情吗?

如果是 Java,我会这样做:

public class AnonymousClass {

    private abstract class LogCallback {

        abstract void sendLog(String log);

        abstract void setErrorCode(int code);
    }

    private class Engine {

        public void setCallback(LogCallback callback) {
            this.callback = callback;
        }

        public void start() {
            if (callback != null) {
                callback.sendLog("Starting...");
            }
        }

        private LogCallback callback;
    }

    public void process() {
        Engine engine = new Engine();
        engine.setCallback(new LogCallback() {
            @Override
            void sendLog(String log) {
                System.out.println("out: " + log);
            }

            @Override
            void setErrorCode(int code) {
                sendLog("error code " + code);
            }
        });
        engine.start();
    }

    public static void main(String[] args) {
        AnonymousClass example = new AnonymousClass();
        example.process();
    }

}

【问题讨论】:

  • 简短的回答是不,你不能。,据我所知,c++ 不提供这样的功能。
  • 使用 std::function.
  • 您的 Java 版本在概念上看起来与您的 C++ 版本非常相似

标签: c++ c++11


【解决方案1】:

霍尔特的上述评论是正确的。在像 Java 的 new X() { ... } 这样的表达式中声明新类型的唯一方法是 lambda 表达式,并且不能从抽象基类派生它。

这样做的 C++ 方法是停止所有 OOP 继承 ickiness 并使用函数模板,或采用类型已删除 std::function 的函数,因此它接受任何具有合适调用签名的可调用对象,然后您可以使用一个 lambda。

例如:

class Engine {
public:
    enum class LogType { String, ErrorCode };
    using LogCallback = std::function<void(LogType, std::string, int)>;
    Engine();
    virtual ~Engine();

    void setCallback(LogCallback callback) { logCallback = callback; }

    void start() {
        if (logCallback)
            logCallback(LogType::String, "Starting...", 0);
    }

private:
    LogCallback logCallback;
};

int anonymous() {
    Engine engine;
    engine.setCallback([](Engine::LogType t, std::string log, int code) {
        if (t == Engine::LogType::ErrorCode)
             log = "error code " + std::to_string(code);
        cerr << "err: " << log << endl;
    });
    engine.start();
}

(LogCallback 类型的更好定义是:

using LogCallback = std::function<void(std::variant<std::string, int>)>;

但我们在 C++ 中还没有 variant。)

【讨论】:

  • 你能举个例子吗?
  • 除了在Engine 中使用LogCallback* logCallback,您还可以使用sendLogsendErrorCode 的2 个std::function 对象作为回调。
  • @rozina,因为 OP 的示例显示了一个回调函数使用另一个回调函数(setErrorCode 使用 sendLog),所以使用一个回调函数来完成这两项工作可能是有意义的。
【解决方案2】:

你不能就地实现接口,但你可以创建带有回调函数作为参数的泛型类:

class GenericLogCallback : public LogCallback {
public:
    GenericLogCallback(std::function<void (std::string)> sendlog,
     std::function<void (int)> seterrorcode) : sendLog_(std::move(sendlog)),
   setErrorCode_(std::move(seterrorcode)) {}

    virtual void sendLog(std::string log) override {
       if(sendLog_) sendLog_(log);
    }
    virtual void setErrorCode(int code) override {
       if(setErrorCode_) setErrorCode_(code);
    }
private:
   std::function<void (std::string)> sendLog_;
   std::function<void (int)> setErrorCode_;
};

...
GenericLogCallback err([](std::string){},[](int){});
engine.setCallback(&err);

【讨论】:

  • 最好使用这个而不是LogCallback,如果你使用std::function隐藏,虚拟调度没有意义。引擎会按价值接受这个
【解决方案3】:

已经在这里使用std::function 发布的答案很好,但如果您非常关心性能,您可以通过直接存储函子(可能是std::bind() 或C 样式函数指针的结果,或 lambdas):

template <typename SendLog, typename SetErrorCode>
class GenericLogger : public LogCallback {
public:
    GenericLogger(SendLog sender, SetErrorCode setter)
        : m_sender(sender), m_setter(setter) {}

    void sendLog(std::string log) override {
        m_sender(log);
    }

    void setErrorCode(int code) override {
        m_setter(code);
    }

    SendLog m_sender;
    SetErrorCode m_setter;
};

template <typename SendLog, typename SetErrorCode>
GenericLogger<SendLog, SetErrorCode> makeLogger(SendLog sender, SetErrorCode setter) {
    return GenericLogger<SendLog, SetErrorCode>(sender, setter);
}

void sendLog(std::string log) {
    std::cout << "out: " << log << std::endl;
}

void setErrorCode(int code) {
    sendLog("error code " + std::to_string(code));
}

int main()
{
    Engine engine;
    auto out = makeLogger(
        [](std::string s){std::cout << "lambda: " << s << '\n';},
        setErrorCode);
    engine.setCallback(&out);
    engine.start();
}

上述内容避免使用std::function,除非makeLogger() 的实际参数属于该类型。这通过精确调用给定的函子来减少开销,而不是总是存储std::function

【讨论】:

    【解决方案4】:

    您可以做的一件事是创建一个允许使用 lambdas 的包装类:

    template<class F1, class F2>
    struct LogCallbackF : LogCallback {
        F1 f1;
        F2 f2;
        LogCallbackF(F1 f1, F2 f2) : f1(std::move(f1)), f2(std::move(f2)) {}
    
        void sendLog(std::string msg) override { f1(msg); }
        void setErrorCode(int code) override { f2(code); }
    };
    
    template<class F1, class F2>
    inline LogCallbackF<F1, F2> make_log_callback(F1 f1, F2 f2) {
        return {std::move(f1), std::move(f2)};
    }
    
    void process() {
        Engine engine;
        auto callback = make_log_callback(
            [](std::string a) { std::cout << a << '\n'; },
            [](int a) { std::cout << a << '\n'; }
            );
        engine.setCallback(&callback);
        engine.start();
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 2012-11-14
      • 1970-01-01
      • 2011-07-22
      相关资源
      最近更新 更多