首页 > 编程语言 >C++工厂模式简易实现

C++工厂模式简易实现

时间:2023-08-05 12:56:03浏览次数:31  
标签:std name Factory C++ 工厂 简易 Cat include string

C++工厂模式简易实现

引言:动态绑定是面向对象编程的重要功能,但C++目前还没有纳入标准库的反射机制,所以为了更方便的动态构造对象,使得通过配置文件的方式改变派生类对象,而不需要去修改代码,所以可以使用工厂这一常见的设计模式,来完成类对象的动态构造。

基于C++11的新特性和模板,实现一个简易的类对象构造工厂,本文实现的工厂产生的类对象都是独立的。(即非单例工厂)

首先,考虑到在一个程序中,始终应该只有一个工厂对象,故我们采用单例模式来实现工厂,其代码如下

#pragma once

#include <unordered_map>
#include <memory>
#include <functional>
#include <string>

// 第一个参数是基类名称 可变参数是构造器的参数列表
template<class Base, class... Args>
class Factory {
public:
	// 禁止外部拷贝和赋值
	Factory(const Factory&) = delete;
	const Factory& operator = (const Factory&) = delete;
	// 获取工厂实例
	static Factory& Instance() {
        // C++11 保证static对象在构造的时候是线程安全的
		static Factory<Base, Args...>* instance = new Factory<Base, Args...>();
		return *instance;
	}
	// 向工厂注册类及其构造器
	void Register(const std::string& name, std::function<std::shared_ptr<Base>(Args...)> constructor) {
		_factory[name] = constructor;
		return;
	}
	// 通过名字构造对象
	std::shared_ptr<Base> Construction(const std::string& name, Args... args) {
		return _factory.find(name) == _factory.end() ? nullptr :
			_factory[name](std::forward<Args>(args)...);
	}
private:
	Factory() = default;
	~Factory() = default; // 禁止外部析构
	// 类名->构造器
	std::unordered_map<std::string, std::function<std::shared_ptr<Base>(Args...)>> _factory;
};

然后为了减少向工厂注册类对象的重复工作量,将该操作进行封装,封装后的类对象如下:

#pragma once

#include "Factory.h"

// 辅助注册类 第一个参数是基类 第二个参数是 派生类 
// 可变参数是派生类构造函数所需要的参数
template<class Base, class Impl, class... Args>
class Register {
public:
	// name是对象的名字,也是工厂中用于映射的key
	explicit Register(const std::string& name) {
		Factory<Base, Args...>& factory = Factory<Base, Args...>::Instance();
		factory.Register(name, [](Args... args) {
			return std::make_shared<Impl>(std::forward<Args>(args)...);
			});
	}
};

以上就是C++工厂的实现,下面来进行测试。

假设基类为Animal,其有两个派生类CatDog。其定义分别如下:

Animal.h内容:

#pragma once

#include <iostream>
#include <string>
#include "Factory.h"


// 基类 声明为纯虚函数
class Animal {
public:
	Animal() = default;
	virtual ~Animal() = default;
	virtual std::string getName() = 0;
private:
};

Dog.h内容:

#pragma once

#include "Animal.h"
#include "Register.h"

class Dog : public Animal {
public:
	Dog() = default;
	Dog(const std::string& _name) : name(_name) {}
	std::string getName() override;
	~Dog() = default;
private:
	std::string name;
};

Dog.cpp内容:

#include "Dog.h"

std::string Dog::getName() {
	return name;
}

namespace {
	// 注册语句建议不要放在.h文件中,可能会导致在编译的时候出现重名
	// 放在.cpp文件并放入匿名namespace中可以将作用域限定在该cpp文件
	// 不会与其他文件产生冲突
	// 本质是定义了一个名字为 _ 的Register对象 因为在cpp文件中
	// 且放入匿名namespace,所以Cat.cpp中同样可以定义一个名字为 _ 的
	// Register对象
	Register<Animal, Dog, std::string> _("Dog");
}

Cat.h内容:

#pragma once

#include "Animal.h"
#include "Register.h"


class Cat : public Animal {
public:
	Cat() = default;
	Cat(const std::string& _name) : name(_name) {}
	std::string getName() override;
	~Cat() = default;
private:
	std::string name;
};

Cat.cpp内容

#include "Cat.h"


std::string Cat::getName() {
	return name;
}

namespace {
	// 同Dog注释
	Register<Animal, Cat, std::string> _("Cat");
}

最后,进行测试,main.cpp文件内容如下:

#include <iostream>
#include <string>

#include "Factory.h"
#include "Cat.h"
#include "Dog.h"

namespace {
	using std::cout;
	using std::endl;
	using std::string;
}

int main() {
	std::shared_ptr<Animal> animal1 = Factory<Animal, std::string>::Instance().Construction("Cat", "I am cat");
	std::shared_ptr<Animal> animal2 = Factory<Animal, std::string>::Instance().Construction("Dog", "I am dog");
	if (animal1 != nullptr && animal1.get() != nullptr) {
		std::cout << animal1->getName() << std::endl;
	}
	if (animal1 != nullptr && animal2.get() != nullptr) {
		std::cout << animal2->getName() << std::endl;
	}
	return 0;
}

输出如下:

I am cat
I am dog

标签:std,name,Factory,C++,工厂,简易,Cat,include,string
From: https://www.cnblogs.com/cherish-/p/17583887.html

相关文章

  • 【转载】C/C++ 通过初始化列表和构造函数内赋值初始化成员变量的区别
    【结论】一、在有些情况下,必须使用初始化列表。特别是const和引用数据成员被初始化时。二、从效率方面来说,对于内置类型或复合类型,差异不会太大,但对于非内置数据类型,差异还是很明显的【具体参考】C/C++通过初始化列表和构造函数内赋值初始化成员变量的区别_Zju_Jemery的博客-......
  • 《C++》机房预约系统案例
    机房预约系统文件可运行存在bug,断断续续手搓10多天Administrator.h#pragmaonce#include"LoginIdentity.h"#include"CompRoom.h"classMapId{public: stringM_name; stringM_pwd;};classLoginAdmin:publicLogin{public: LoginAdmin(); LoginAdmin(stri......
  • C++ 核心指南之 C++ 哲学/基本理念(下)
    C++核心指南(C++CoreGuidelines)是由BjarneStroustrup、HerbSutter等顶尖C+专家创建的一份C++指南、规则及最佳实践。旨在帮助大家正确、高效地使用“现代C++”。这份指南侧重于接口、资源管理、内存管理、并发等High-level主题。遵循这些规则可以最大程度地保证静......
  • day 122 - bean的作用域,生命周期,工厂模式
    bean的作用域在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围singleton(默认)在IOC容器中,这个bean的对象始终为单实例在ioc容器初始化时创建对象prototype这个bean在IOC容器中有多个实例在获取bean时创建对象<!--scope设置bean的作用域:......
  • c++算法之离散化例题
    离散化基础2题目描述给定 n 个元素的数列,将相同的数据离散化为一个数据(去重),即把 {4000,201,11,45,11}{4000,201,11,45,11} 离散化为 {4,3,1,2,1}{4,3,1,2,1}。输入格式第一行一个整数 (1≤m≤105)n(1≤n≤105),为元素的个数。第二行 n 个用空格隔天的整数a[i](−109......
  • 《Modern C++ Design》之上篇
    如下内容是在看侯捷老师翻译的《ModernC++Design》书籍时,整理的code和摘要,用于不断地温故知新。第一章1.运用TemplateTemplate参数实作PolicyClassestemplate<template<classCreated>classCreationPolicy>//template<template<class>classCreationPolicy......
  • 一些有趣的C++代码
    本文混合搅碎剁烂转载。。。 1:绘制曲线 #include<bits/stdc++.h>usingnamespacestd;intmain(){intx,m;for(doublei=1;i>=-1;i-=0.1){m=acos(i)*10;for(x=1;x<m;x++)cout<<"";cout<&l......
  • chromedp+goquery简易抓取百度热搜
    安装gogetgithub.com/wms3001/datacrawling抓取百度热搜信息varbnBaiDuHotNewsres:=bn.GetBaiDuHostNews()log.Println(res)......
  • C++多线程中互斥量的使用
    多线程中互斥信号量(Mutex)的使用1.0互斥量的基本概念1.1Example\(\quad\)首先我们要明白,为什么会有互斥信号量的出现,在多线程编程中,不同的线程之间往往要对同一个数据进行操作,如果该数据是只读的,当然不会出现什么问题,但是如果两个线程同时对某个数据进行写操作,则可能出现难以......
  • 使用Vue+Vite搭建在线 C++ 源代码混淆工具,带在线实例
    就酱紫github开源地址:https://github.com/dffxd-suntra/cppdgithub在线实例:https://dffxd-suntra.github.io/cppd/预览图片:长截屏背景图重复了,抱歉......