如何创建抽象类的内联实现?

时间:2016-05-10 10:10:49

标签: c++ c++11

假设我有以下回调类:

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

}

4 个答案:

答案 0 :(得分:2)

霍尔特上面的评论是正确的。在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。)

答案 1 :(得分:1)

您无法实现接口,但您可以创建具有回调函数作为参数的泛型类:

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

答案 2 :(得分:1)

使用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

答案 3 :(得分:1)

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

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<table id="usersdata" > </table>