首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何跨库共享c++中的对象

如何跨库共享c++中的对象
EN

Stack Overflow用户
提问于 2021-01-21 16:55:25
回答 1查看 130关注 0票数 0

假设我有这样一个程序:

文件main.cpp

代码语言:javascript
复制
#include "something.hpp"

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

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

文件logger.hpp

代码语言:javascript
复制
#include <iostream>

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

文件logger.cpp

代码语言:javascript
复制
#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

代码语言:javascript
复制
#include "logger.hpp"

class Something {
    public:
        Something();
};

文件something.cpp

代码语言:javascript
复制
#include "something.hpp"

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

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

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-01-22 13:30:36

第一种方法: Singleton

正如注释中所讨论的,您可以使用单例设计模式来解决这个问题。但是,请记住这个模式有几个缺点,其中两个是:

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

在编写高质量的软件时,这是真正的问题。另外,对于您的特殊情况,请确保阅读这个答案,这说明了如何确保所有内容都适当地链接,这样就不会有多个单例实例。

第二种方法:依赖注入

我决定在这里发布一个答案,以说明另一种解决上述两个问题的方法:依赖注入 (DI)。使用DI时,不需要创建依赖项,而是通过参数注入依赖项。例如,而不是:

代码语言:javascript
复制
Something::Something() {
    auto logger = new Logger(); // Dependency creation (not injection)
    logger->log("hello !");
}

你会得到这样的东西:

代码语言:javascript
复制
Something::Something(Logger* p_logger) { // Logger dependency injected through p_logger
    p_logger->log("hello !");
}

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

您可以通过抽象您的依赖关系将其提升到另一个级别。例如,您可以为您的Logger类编写一个接口并使用它:

代码语言:javascript
复制
// 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可能如下所示:

代码语言:javascript
复制
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的一个可能的缺点是,最终可能会有很多参数,但在我看来,它仍然优于使用单例。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65832122

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档