微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何跨库共享 C++ 中的对象 文件 main.cpp文件记录器.hpp文件记录器.cpp文件 something.hpp文件 something.cpp

如何解决如何跨库共享 C++ 中的对象 文件 main.cpp文件记录器.hpp文件记录器.cpp文件 something.hpp文件 something.cpp

假设我有一个这样的程序:

文件 main.cpp

#include "something.hpp"

int main(int argc,char* argv[]) {
    some = new Something();
    return 0;
}

链接到由以下文件组成的 .so 库:

文件记录器.hpp

#include <iostream>

class Logger {
    public:
        Logger();
        void log(char);
        void set_name(char);
    private:
        char m_name;
};

文件记录器.cpp

#include "logger.hpp"

Logger::Logger() {}
void Logger::log(char msg) {
     std::cout << this->m_name << " : " << msg;
}
void Logger::set_name(char name) {
    this->m_name = name;
}

文件 something.hpp

#include "logger.hpp"

class Something {
    public:
        Something();
};

文件 something.cpp

#include "something.hpp"

Something::Something() {
    logger->log("hello !");
}

现在的代码将在 something.cpp 处的 logger->log() 中失败,因为从未定义 logger。我可以通过添加 logger = new Logger()解决这个问题。但我只想创建一个新的 Logger 实例,如果没有在使用此库的程序/库中创建。如果已经创建了一个实例,我可以通过添加 extern Logger logger; 来使用它。但是当没有实例被创建时,这将不起作用。任何建议(有可能吗?)?

注意:我已经在使用 Gtkmm4 / Glibmm2.6,也许有使用 Gtk 或 Glib 的解决方案...

解决方法

第一种方法:单身

正如评论中所讨论的,您可以使用 Singleton design pattern 实现这一目标。但是,请记住,此模式具有 several drawbacks, 其中两个是:

  • 单例允许全局访问。
  • 单例很难进行单元测试。

在编写高质量软件时哪些是真正的问题。此外,对于您的特定 情况下,请务必阅读 this answer,它解释了如何确保一切 被适当地链接,所以你最终不会得到你的多个实例 单身。

第二种方法:依赖注入

我决定在这里发布一个答案来说明另一种做事方式 解决了上面提到的两个问题:dependency injection (DI)。有了DI, 您不创建依赖项,而是通过参数注入它们。为了 例如,而不是:

Something::Something() {
    auto logger = new Logger(); // Dependency creation (not injection)
    logger->log("hello !");
}

你会有类似的东西:

Something::Something(Logger* p_logger) { // Logger dependency injected through p_logger
    p_logger->log("hello !");
}

请注意,DI 本身并不能解决“一个实例”问题。必须注意 创建您的依赖项一次(通常在您的 main 中),然后将它们作为 使用它们的参数。但是,全局访问问题已解决。

您可以通过抽象您的依赖项将其提升到另一个层次。例如, 您可以为 Logger 类编写一个接口并改用它:

// Somewhere in your library:
class ILogger
{
public:
    virtual ~ILogger() = default;
    virtual void log(const std::string& p_message) = 0;
    virtual void set_name(const std::string& p_name) = 0;
};

// In Logger.hpp:
class Logger : public ILogger {
    public:
        Logger();

        void log(const std::string& p_message) override;
        void set_name(const std::string& p_name) override;

    private:
        std::string m_name;
};

// In something.hpp/cpp:
Something::Something(ILogger* p_logger) { // Logger dependency injected through p_logger
    p_logger->log("hello !");
}

要实现这一点,您的 main 可能如下所示:

int main(int argc,char* argv[]) {
    // Here,you create your logger dependency:
    std::unique_ptr<ILogger> concreteLogger = std::make_unique<Logger>();
    concreteLogger->set_name("frederic");

    // Here,you inject it. From here on,you will inject it everywhere
    // in your code. The using code will have no idea that under the hood,// you really are using the Logger implementation:
    some = new Something(concreteLogger.get());

    // Note: if you use `new`,do not forget to use `delete` as well. Otherwise,//       check out std::unique_ptr,like above.

    return 0;
}

这样做的好处是您现在可以更改记录器的实现 任何时候都不需要(除了main)关心它。您还可以创建 如果您想对 Something 进行单元测试,请模拟您的记录器。这是高度 比处理单元测试中的单例更灵活,这在术语中会 创建各种(难以调查/解决)问题。就这而言,这解决了 上面提到的第二个问题。

请注意,DI 的一个可能缺点是您最终可能会拥有大量 参数,但在我看来它仍然优于使用单例。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。