一、具体情况
在前面的文章“名称反射创建对象的一种实现”发布后,有同学问为什么在实例中既给了模板的名称,又给了字符串名称,这还有什么意义呢?这个问题非常好,当时一时偷懒,没有把事情说清楚,这篇文章算是对提问同学的一个回答。
其实很简单,如果把实现的类名和声明的类型换成不同的类,同时这个需要反射的类必须继承前面提到的ClassInstance,再在给定的类(即Worker)中实现一个创建,那么整个反射的过程就符合了一般反射的习惯。
二、补充例程
基于上面的分析,看下面的代码分析即可明白:
......
//测试类
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; }
};
int main() {
//重点看这里的声明和后面的获取,即下面的两行代码
ClassInstance<Worker, int, int> dc;
auto* pp = InstanceFactory<Worker, int, int>::Instance()->CreateInstance("Worker", 1, 2);
pp->display();
return 0;
}
上面就上次发的代码的问题提出部分,这次完善一下代码:
//实例模板
template <typename T, typename... Args>
class ClassInstance {
public:
struct RegContain {
RegContain() {
char* sName = nullptr;
std::string className;
//注意:这是Linux平台,其它平台需要处理相关类名
// sName = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
sName = name;
if (nullptr != sName) {
//className = sName;
//free(sName);
}
className = name;
InstanceFactory<T, Args...>::Instance()->RegClass(className, CreateObject);
}
inline void done() const {};
};
public:
ClassInstance() {
//this->InstanceFromName();
reg_.done(); // 确保调用前模板实例化,请参看前面的模板延迟加载
}
~ClassInstance() {};
public:
// 变参创建实例
static T* CreateObject(Args &&...args) {
T* pIns = nullptr;
try {
pIns = new T(std::forward<Args>(args)...);
}
catch (...) {
return nullptr;
}
return pIns;
}
private:
static RegContain reg_;
};
template <typename T, typename... Args>
typename ClassInstance<T, Args...>::RegContain ClassInstance<T, Args...>::reg_;
//测试类
class Worker :public ClassInstance<Worker,int,int> {
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 ObjIns {
public:
template<typename ...Targs>
Worker* GetObj(const std::string& name, Targs&&... args)
{
Worker* p = InstanceFactory<Worker,Targs...>::Instance()->CreateInstance(name, std::forward<Targs>(args)...);
return p;
}
};
int main() {
ObjIns o;
auto *pp = o.GetObj("Worker",3,6);
pp->display();
}
这里的代码主要修改了两处,一处是将实例代码的生成InstanceFactory从函数转到了内部类,确保模板在调用函数前的实例化(当然也可以使用其它的方式);另外一个就是将实例的应用封装了一层,即原来的Worker不直接暴露,而是通过一个对象类ObjIns来动态创建,这样就更符合开发者对反射使用的要求。
有的同学可能会问,这个还是有问题啊,每次都得创建一个新类的构建函数来实现啊,同学可以想一想,是不是在某种场景下,可以使用继承?或者干脆把功能从Worker中单独提取出去?再使用继承!自己可以试试,网上很多的代码其实也是这个思路。
三、总结
这个怪偷懒的精神做祟,说明一下就好了。这也说明,对细节的说明一定要清晰严谨,不能想当然的别人也会这么理解。沟通的成本,就是这样产生的。谢谢热心同学“ω劉εз珩”的指出,非常感谢,共同进步。