C++笔记之智能指针和单例、依赖注入结合使用

news2024/9/28 13:23:39

C++笔记之智能指针和单例、依赖注入结合使用

code review!

文章目录

  • C++笔记之智能指针和单例、依赖注入结合使用
    • 例1.一个类不使用单例,另一个类使用单例
    • 例2.两个类都使用单例,并且通过getInstance()传入类的实例
    • 例3.std::make_unique不能访问私有的构造函数
    • 例4.不通过友元,通过静态成员函数在类外部调用类私有的构造函数

在这里插入图片描述

例1.一个类不使用单例,另一个类使用单例

在这里插入图片描述

运行
在这里插入图片描述

代码

#include <iostream>
#include <memory>

class Logger {
private:
    Logger() {}  // 私有构造函数,防止外部实例化

public:
    static Logger& getInstance() {
        static Logger instance;  // 静态局部变量确保只有一个实例
        return instance;
    }

    void log(const std::string& message) {
        std::cout << "Log: " << message << std::endl;
    }
};

class Service {
private:
    std::shared_ptr<Logger> logger;

public:
    Service(std::shared_ptr<Logger> logger) : logger(logger) {}

    void doSomething() {
        logger->log("Service is doing something.");
    }
};

int main() {
    // 在依赖注入的方式下,创建 Service 实例并传入 Logger 实例
    Service service(std::make_shared<Logger>(Logger::getInstance()));

    // 使用 Service 实例
    service.doSomething();

    return 0;
}

例2.两个类都使用单例,并且通过getInstance()传入类的实例

在这里插入图片描述

运行
在这里插入图片描述

代码

#include <iostream>
#include <memory>

class Logger {
  private:
    Logger() {} // 私有构造函数,防止外部实例化

  public:
    static std::shared_ptr<Logger> getInstance() {
        static std::shared_ptr<Logger> instance = std::shared_ptr<Logger>(new Logger());
        return instance;
    }

    void log(const std::string &message) {
        std::cout << "Log: " << message << std::endl;
    }
};

class Service {
  private:
    std::shared_ptr<Logger> logger;

    Service(const std::shared_ptr<Logger> &logger) : logger(logger) {}

  public:
    static std::shared_ptr<Service> getInstance(const std::shared_ptr<Logger> &logger) {
        static std::shared_ptr<Service> instance = std::shared_ptr<Service>(new Service(logger));
        return instance;
    }

    void doSomething() {
        logger->log("Service is doing something.");
    }
};

int main() {
    // 创建 Logger 实例(智能指针管理)
    std::shared_ptr<Logger> logger = Logger::getInstance();

    // 创建 Service 实例并传入 Logger 实例
    std::shared_ptr<Service> service = Service::getInstance(logger);

    // 使用 Logger 实例
    logger->log("Logging from main");

    // 使用 Service 实例
    service->doSomething();

    return 0;
}

例3.std::make_unique不能访问私有的构造函数

在这里插入图片描述

编译报错:
在这里插入图片描述

这段代码存在一些小问题:

Service 类的 getInstance 方法中,你使用了 std::make_unique 来创建一个 Service 实例,但是 Service 的构造函数是私有的,不能直接使用 make_unique。你应该改用 std::shared_ptr<Service>(new Service(logger)) 来创建实例,就像在 Logger 类中的 getInstance 方法中一样。

代码

#include <iostream>
#include <memory>

class Logger {
  private:
    Logger() {} // 私有构造函数,防止外部实例化

  public:
    static std::shared_ptr<Logger> getInstance() {
        static std::shared_ptr<Logger> instance = std::shared_ptr<Logger>(new Logger());
        return instance;
    }

    void log(const std::string &message) {
        std::cout << "Log: " << message << std::endl;
    }
};

class Service {
  private:
    std::shared_ptr<Logger> logger;

    Service(const std::shared_ptr<Logger> &logger) : logger(logger) {}

  public:
    static std::shared_ptr<Service> getInstance(const std::shared_ptr<Logger> &logger) {
        static std::shared_ptr<Service> instance = std::make_unique<Service>(logger);
        return instance;
    }

    void doSomething() {
        logger->log("Service is doing something.");
    }
};

int main() {
    // 创建 Logger 实例(智能指针管理)
    std::shared_ptr<Logger> logger = Logger::getInstance();

    // 创建 Service 实例并传入 Logger 实例
    std::shared_ptr<Service> service = Service::getInstance(logger);

    // 使用 Logger 实例
    logger->log("Logging from main");

    // 使用 Service 实例
    service->doSomething();

    return 0;
}

例4.不通过友元,通过静态成员函数在类外部调用类私有的构造函数

在这里插入图片描述

运行
在这里插入图片描述

将实例的创建和初始化封装在了名为 createInstance 的公共静态成员函数中。这个函数在类的外部被调用时,会调用私有构造函数创建一个实例,并返回一个指向这个实例的智能指针。

通过这种方式,你不需要将 std::make_unique 声明为友元函数,也不需要使用特定的友元声明。而是通过公共静态成员函数间接地在类的外部创建实例,从而绕过了直接访问私有构造函数的问题。

构造函数不是静态成员,它们属于实例化的过程,而不是类本身的静态成员。

在C++中,构造函数不是静态成员,它们是用于创建类的实例的特殊成员函数。私有构造函数意味着不能在类的外部直接调用它来创建实例。这是单例模式的一个关键概念,它确保只有一个实例被创建。

而使用公共静态成员函数(如 createInstance)的优势在于,它可以在类的外部创建实例。这是因为静态成员函数不依赖于特定实例,所以它们可以在没有创建对象的情况下被调用。通过在公共静态成员函数中调用私有构造函数,你可以在类的外部创建类的实例。

所以,使用公共静态成员函数解决单例模式中私有构造函数访问问题的关键在于,它提供了一种通过类的内部机制(即静态成员函数)来间接创建实例的方式,绕过了私有构造函数不能直接在外部调用的限制。

代码

#include <iostream>
#include <memory>

class Dependency {
  public:
    void doSomething() {
        std::cout << "Dependency is doing something." << std::endl;
    }
};

class Singleton {
  private:
    Singleton() { /* 构造函数私有化 */
    }

    static std::unique_ptr<Singleton> instance;
    std::unique_ptr<Dependency> dependency; // 保存依赖项的指针

  public:
    // 创建 Singleton 实例的公共静态函数
    static std::unique_ptr<Singleton> createInstance() {
        return std::unique_ptr<Singleton>(new Singleton);
    }

    // 获取 Singleton 实例的公共静态函数
    static Singleton &getInstance() {
        if (!instance) {
            instance = createInstance();                           // 创建实例
            instance->dependency = std::make_unique<Dependency>(); // 初始化依赖项
        }
        return *instance;
    }

    void useDependency() {
        dependency->doSomething();
    }

    // 其他成员函数
};

// 初始化静态成员变量
std::unique_ptr<Singleton> Singleton::instance = nullptr;

int main() {
    // 创建 Singleton 实例
    std::unique_ptr<Singleton> singleton = Singleton::createInstance();

    // 使用注入的依赖项进行操作
    singleton->useDependency();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/929235.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python将png格式的图片转换为jpg格式的图片

png图片是4通道 RGBA图像&#xff0c;具有4个通道&#xff08;红色、绿色、蓝色和透明度&#xff09;&#xff0c;用于表示彩色图像以及透明度信息。 只是简单的修改后缀&#xff0c;并不能将png格式图片改为jpg格式。 将png格式的图片转换为jpg格式的图片 确保安装了pillow库…

自启动遇到某个节点或者某种环境变量问题导致启动失败

前言&#xff1a;此次记录无人车自启动过程遇到的问题。为了让ROS无人车能够实现飞控进行室外自主航线的问题&#xff0c;将飞控发布的PWM转为ROS无人车对应的速度。为了确保无人车启动后能够使用遥控器控制无人车&#xff0c;所以需要开机自启动。 硬件&#xff1a; 1、star…

C语言刷题训练DAY.13

1.有序序列判断 解题思路&#xff1a; 这里我们先看代码&#xff0c;我们定义了一个flag1和flag2&#xff0c;它的作用主要就是判断是不是升序&#xff0c;具体怎么使用的&#xff0c;我为大家画图展示。 解题代码&#xff1a; #include<stdio.h> int main() {int n 0;…

springboot源码编译问题

问题一 Could not find artifact org.springframework.boot:spring-boot-starter-parent:pom:2.2.5.RELEASE in nexus-aliyun (http://maven.aliyun.com/nexus/content/groups/public/) 意思是无法在阿里云的镜像仓库中找到资源 解决&#xff1a;将配置的镜像删除即可&#…

《代码随想录》专题:回溯算法1

1、组合问题 题目链接&#xff1a;77. 组合 题解 把组合问题抽象为如下树形结构 图中每次搜索到了叶子节点&#xff0c;我们就找到了一个结果。相当于只需要把达到叶子节点的结果收集起来&#xff0c;就可以求得 n个数中k个数的组合集合。最难理解的是单层搜索的过程 回溯法的…

NIST SP 800-22测试包

NIST测试包,包含15种不同的测试,对序列的随机性进行测试。其中,部分测试包含子测试。 随便选一个序列,看看测试结果: ------------------------------------------------------------------------------ RESULTS FOR THE UNIFORMITY OF P-VALUES AND THE PROPORTION OF P…

Linux学习之Ubuntu 20.04在github下载源码安装Openresty 1.19.3.1

参考的博文&#xff1a;《在 Ubuntu 上使用源码安装 OpenResty》 《OpenResty 安装安装详解-Ubuntu》 《Linux学习之CentOS 7源码安装openresty》 https://openresty.org/en/download.html是官网下载网址&#xff0c;页面往下拉有下载的链接。 https://github.com/openresty…

表达式(Expression)

可以将lambda表达式分配给Func或Action类型委托&#xff0c;以处理内存中的集合。.NET编译器在编译时将分配给Func或Action类型委托的lambda表达式转换为可执行代码。 LINQ引入了一种名为Expression的新类型&#xff0c;该类型代表强类型的lambda表达式。这意味着lambda表达式也…

Vue2向Vue3过度核心技术工程化开发和脚手架

目录 1 工程化开发和脚手架1.1 开发Vue的两种方式1.2.脚手架Vue CLI 2 项目目录介绍和运行流程2.1 项目目录介绍2.2 运行流程 3 组件化开发4 根组件 App.vue4.1 根组件介绍4.2 组件是由三部分构成4.3 总结 5 普通组件的注册使用-局部注册5.1 特点&#xff1a;5.2 步骤&#xff…

redis持久化机制 事务详解

目录 前言&#xff1a; 持久化机制 RDB&#xff08;Redis DataBase&#xff09; 手动触发 save bgsave 自动触发 RDB特点 AOF&#xff08;append only file&#xff09; 缓冲区刷新策略 重写机制 aof重写流程 混合持久化 事务 事务操作命令 WATCH WATCH实现原…

【前端从0开始】HTML5+CSS3基础语法

html5发展 HTML5是HTML最新的修订版本&#xff0c;2014年10月由万维网联盟&#xff08;W3C&#xff09;完成标准制定。 HTML5 仍处于完善之中。然而&#xff0c;大部分现代浏览器已经具备了某些 HTML5 支持 h5添加css hack&#xff0c;css zoom知识点 特性 新的特殊内容元素…

【洛谷】P2440 木材加工

原题链接&#xff1a;https://www.luogu.com.cn/problem/P2440 1. 题目描述 2. 思路分析 整体思路&#xff1a;二分答案 设置一个变量longest来记录最长木头的长度&#xff0c;sum记录切成的小段数量之和。 令左边界l0&#xff0c;右边界llongest。 写一个bool类型的check…

函数指针.

首先看一段代码&#xff1a; #include <stdio.h> void test() {printf("hehe\n"); } int main() {printf("%p\n", test);printf("%p\n", &test);return 0; } 输出结果&#xff1a; 输出的是两个地址&#xff0c;这两个地址是 test 函…

VMware 中Centos8的NAT网络设置

1、先将虚拟机设置为NAT模式 2、打开虚拟网络编辑器&#xff0c;记录以下信息 NAT设置&#xff1a;子网掩码、网关 DHCP设置&#xff1a;I P 范围 (自动时) 3、进入Centos8的网络设置页面&#xff0c;按照记录的信息进行配置 4、重载、重启网卡 nmcli c reload ensl60 n…

【位运算】算法实战

文章目录 一、算法原理常见的位运算总结 二、算法实战1. leetcode面试题01.01. 判断字符是否唯一2. leetcode268 丢失的数字3. leetcode371 两整数之和4. leetcode004 只出现一次的数字II5. leetcode面试题17.19. 消失的两个数字 三、总结 一、算法原理 计算机中的数据都以二进…

基础论文学习(5)——MAE

MAE&#xff1a;Masked Autoencoders Are Scalable Vision Learners Self-Supervised Learning step1&#xff1a;先用无标签数据集&#xff0c;把参数从一张白纸训练到初步预训练模型&#xff0c;可以得到数据的 Visual Representationstep2&#xff1a;再从初步成型&#x…

OpenCV基础知识(7)— 腐蚀与膨胀

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。腐蚀和膨胀是图像形态学中的两种核心操作&#xff0c;通过这两种操作可以清除或者强化图像中的细节。本节课就对OpenCV中的腐蚀和膨胀操作进行详细的介绍。&#x1f308; 前期回顾&#xff1a; OpenCV基础知识&#xff08;…

Web应用登录验证的几种方式

一、SessionCookie登录 传统的sessioncookie登录是一种有状态 的登录 1、传统的sessioncookie 流程 浏览器登录发送账号和密码&#xff0c;服务端查找数据库验证用户验证成功后&#xff0c;服务端把用户状态&#xff08;登录状态&#xff0c;角色&#xff0c;权限等信息&…

Vue2向Vue3过度核心技术生命周期

目录 1 Vue生命周期2 Vue生命周期钩子3 生命周期钩子小案例1.1 在created中发送数据1.2 在mounted中获取焦点 4 案例-小黑记账清单4.1 需求图示&#xff1a;4.2 需求分析3.思路分析4.代码准备 1 Vue生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#…

【管理运筹学】第 6 章 | 运输问题(1,运输问题的数学模型及性质特点)

文章目录 引言一、运输问题的数学模型及特点1.1 运输问题的数学模型1.2 运输问题的特点1.3 运输问题的解 二、表上作业法写在最后 引言 在之前的学习过程中&#xff0c;我们接触的是较为一般性的线性规划问题。但是随着人们对运输——将人或物由一个空间位置移动到另一个空间位…