一、对象的创建
在传统的编程手段里,创建一个对象,基本就是直接创建。几乎所有的语言都使用这种方式 。在C++的有两种方式,一种是直接创建个对象;另外一个是通过new创建一个对象并获取其指针。他们的本质都是可以得到一个对象。
而在实际的应用需求中,可能需要一种情况:就是需要通过类的名字来动态创建一个对象并投入使用。这种方式其实如果要求不是很复杂,也可以通过类似if…else之类的判断来生成。但是这样的解决方式有它的局限性,如果类比较多,比如十几个甚至几十个。另外,如果这些类的创建参数不同,也都比较难以处理。所以,这里就出现了一个问题,如何能够兼容的写出一个简单的框架来动态创建对象。
这其实也是反射里提出的一种要求,实现了它,也就是实现了通过反射创建对象的过程。
二、分析
通过上面的基本说明,可以做进一步的分析。首先,创建的这些类如果有一些共性,那么可以使用一个基类来控制这些类为其子类,那么,创建的返回结果就形成了创建一种类的这种普遍的情况;其次,如果这些类参数不同,可以把其拆解成不带参数或者统一的某个固定参数的,然后在每个类中增加init的函数,二次传参。这两种情况几乎都可以适应常见的很多种实际应用场景了。但是,可不可以直接使用不同的创建形式不做任何变换来实现呢?当然可以。
也是有两种情况:
1、将构造函数注册到一个统一的模板函数里
此处不介绍这种方法,比较麻烦,有兴趣可以自己试试。原理其实挺简单,就是声明一个函数对象(std::function)为统一的无参方式,然后其实现的函数体内调用变参模板并注册相关参数。这种方式不太友好,所以此处不就提供实际的例程了。
2、将构造函数直接保存使用
即使用一个map来保存类名和构造函数的键值对即可。这种比较好理解,也容易为大家所使用。
这里面都提出了一个问题,如何解决参数的不同的问题?以及不同的类型动态创建如何解决?这两个问题,前一个可以通过变参模板来实现,后一个可以使用模板的动态生成(全特化的不同)来实现。后者已经有了一些元编程的味道。
三、例程
基于上面的分析,看下面的代码分析即可明白:
//#include <cxxabi.h>
#include <functional>
#include <iostream>
#include <unordered_map>
char name[] = "Worker";
class Worker;
//实例工厂
template <typename T, typename... Args>
class InstanceFactory
{
public:
static InstanceFactory* Instance() {
if (nullptr == pInsFactory) {
pInsFactory = new InstanceFactory();
}
return pInsFactory;
}
~InstanceFactory() {};
public:
// 注册
bool RegClass(const std::string& className, std::function<T* (Args &&...args)> func) {
if (nullptr == func) {
return false;
}
return insFunc_.emplace(className, func).second;
}
//通过类名创建实例
T* CreateInstance(const std::string& className, Args &&...args) {
if (insFunc_.count(className) > 0)
{
return insFunc_[className](std::forward<Args>(args)...);
}
return nullptr;
}
private:
InstanceFactory() {};
static InstanceFactory<T, Args...>* pInsFactory;
std::unordered_map<std::string, std::function<T* (Args &&...)>> insFunc_;
};
template <typename T, typename... Args>
InstanceFactory<T, Args...>* InstanceFactory<T, Args...>::pInsFactory = nullptr;
//实例模板
template <typename T, typename... Args>
class ClassInstance {
public:
ClassInstance() {
this->InstanceFromName();
}
~ClassInstance() {};
public:
bool InstanceFromName()
{
char* sName = nullptr;
std::string className;
//注意:这是Linux平台,其它平台需要处理相关类名
sName = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
if (nullptr != sName) {
className = sName;
free(sName);
className = sName;
InstanceFactory<T, Args...>::Instance()->RegClass(className, CreateObject);
return true;
}
return false;
}
// 变参创建实例
static T* CreateObject(Args &&...args) {
T* pIns = nullptr;
try {
pIns = new T(std::forward<Args>(args)...);
}
catch (...) {
return nullptr;
}
return pIns;
}
private:
};
//测试类
class Worker {
public:
Worker() {}
Worker(int a, int b) : a_(a), b_(b) {}
public:
void display() { std::cout << "-this is Worker display function!--:" << a_ + b_ << std::endl; }
private:
int a_ = 0;
int b_ = 0;
};
class Worker1 {
public:
Worker1() {}
Worker1(int a) {}
public:
void display1() { std::cout << "--this is worker1 display1 function--" << std::endl; }
};
class Teacher {
public:
Teacher() {}
Teacher(int a, int b) : a_(a), b_(b) {}
public:
void pDisplay(int t) { std::cout << "--this is Teacher pDisplay--" << a_ + b_ + t << std::endl; }
private:
int a_ = 0;
int b_ = 0;
};
int main() {
ClassInstance<Worker, int, int> dc;
auto* pp = InstanceFactory<Worker, int, int>::Instance()->CreateInstance("Worker", 1, 2);
pp->display();
ClassInstance<Worker1, int> dc1;
auto* pp1 = InstanceFactory<Worker1, int>::Instance()->CreateInstance("Worker1", 1);
pp1->display1();
auto* pponce = InstanceFactory<Worker, int, int>::Instance()->CreateInstance("Worker", 2, 3);
pponce->display();
ClassInstance<Teacher, int, int> dc2;
auto* pT = InstanceFactory<Teacher, int, int>::Instance()->CreateInstance("Teacher", 2, 3);
pT->pDisplay(3);
return 0;
}
上面的代码的知识点就是两个,变参模板和模板动态生成。如果不明白,把代码拷贝到网上的一些可以编译成中间代码的网站上,立刻就清晰了。网上还有利用内部类实现动态注册的,但那个要小心模板的延迟加载(所以在模板生成时需要执行一个看似无意义的函数)。
四、总结
在后期的文章和实例中,大家会发现,会越来越多的把前面的知识融汇贯通,有可能再加上一些新知识点,就会实现一个比较好的技术点。所有的技术应用,一定是在基础技术上的不断的抽象升华。一种完全全新的,不以前边的技术为基础的技术,几乎很难遇到。或者如此说,即使遇到了,也可以通过老的技术快速的体会到新技术的优缺点。
但这有一个前提,思想必须永远是开放包容,与时俱进!