跟我学C++高级篇——名称反射创建对象的一种实现

一、对象的创建

在传统的编程手段里,创建一个对象,基本就是直接创建。几乎所有的语言都使用这种方式 。在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;
}

上面的代码的知识点就是两个,变参模板和模板动态生成。如果不明白,把代码拷贝到网上的一些可以编译成中间代码的网站上,立刻就清晰了。网上还有利用内部类实现动态注册的,但那个要小心模板的延迟加载(所以在模板生成时需要执行一个看似无意义的函数)。

四、总结

在后期的文章和实例中,大家会发现,会越来越多的把前面的知识融汇贯通,有可能再加上一些新知识点,就会实现一个比较好的技术点。所有的技术应用,一定是在基础技术上的不断的抽象升华。一种完全全新的,不以前边的技术为基础的技术,几乎很难遇到。或者如此说,即使遇到了,也可以通过老的技术快速的体会到新技术的优缺点。
但这有一个前提,思想必须永远是开放包容,与时俱进!

相关推荐

  1. c++高级——常见反射框架

    2024-04-22 13:14:04       11 阅读
  2. C++中级——函数模板匹配

    2024-04-22 13:14:04       32 阅读
  3. c++中级——再谈C++20中协程

    2024-04-22 13:14:04       25 阅读
  4. c++中级——面向切片编程

    2024-04-22 13:14:04       39 阅读
  5. C++中级——内联

    2024-04-22 13:14:04       8 阅读
  6. C++中级——零长度数组

    2024-04-22 13:14:04       10 阅读
  7. C++中级——内联补遗

    2024-04-22 13:14:04       12 阅读
  8. C++中级——STL删除

    2024-04-22 13:14:04       21 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-22 13:14:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-22 13:14:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-22 13:14:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-22 13:14:04       18 阅读

热门阅读

  1. shell 脚本基础练习

    2024-04-22 13:14:04       16 阅读
  2. Saas系统多任务分发排队

    2024-04-22 13:14:04       18 阅读
  3. Windows上构建 Chisel-Bootcamp

    2024-04-22 13:14:04       18 阅读
  4. 低配置的电脑上刷新WPF DataGrid 很卡,如何优化?

    2024-04-22 13:14:04       15 阅读
  5. /sys/class/dmi/id/目录文件详解,查看系统硬件信息

    2024-04-22 13:14:04       16 阅读
  6. .NET/C#汇总 —— WPF

    2024-04-22 13:14:04       12 阅读