• C++动态创建对象


    C++动态创建对象

    C++没有反射,但博主接触的一个工程出现了很多读取配置文件然后实例化对象的情况,因此探究了一下用C++动态创建对象,这对理解Java的getInstanceByName也有好处

    1. 思想

    三层结构:

    • Class: 实现具体功能(实现类)
    • ClassRegister: 实现NewInstance接口(注册类)
    • Factory: 实现getInstanceByName,通过map结构,实现一对多(工厂类)

    每个Class对应一个ClassRegister,一个Factory对应了多个ClassRegister.

    1. 调用Factory.getInstanceByName("ClassA")
    2. 查找map,Factory返回ClassARegister
    3. 调用ClassARegister的NewInstance接口,返回具体的A对象的指针
    

    2. 实现

    //Shape.h
    // 具体的功能实现类
    #ifndef _SHAPE_H_
    #define _SHAPE_H_
    
    class Shape
    {
    public:
        virtual void Draw() = 0;
        virtual ~Shape() {}
    };
    
    class Circle : public Shape
    {
    public:
        void Draw();
        ~Circle(){};
    };
    
    class Square : public Shape
    {
    public:
        void Draw();
        ~Square(){};
    };
    
    class Rectangle : public Shape
    {
    public:
        void Draw();
        ~Rectangle(){};
    };
    
    #endif // _SHAPE_H_
    
    //Shape.cpp
    #include "Shape.h"
    #include "DynBase.h"
    #include <iostream>
    
    void Circle::Draw()
    {
        std::cout << "Circle" << std::endl;
    }
    
    void Square::Draw()
    {
        std::cout << "Square" << std::endl;
    }
    
    void Rectangle::Draw()
    {
        std::cout << "Rectangle" << endl;
    }
    
    // 这里使用了DynBase.h中的宏,注册具体的实现类
    REGISTER_CLASS(Circle);
    REGISTER_CLASS(Square);
    REGISTER_CLASS(Rectangle);
    
    //DynBase.h
    #ifndef _DYN_BASE_H_
    #define _DYN_BASE_H_
    
    #include <map>
    #include <string>
    using namespace std;
    
    // 函数指针
    typedef void *(*CREATE_FUNC)();
    
    class DynObjectFactory
    {
    public:
        // 通过map寻找对应的注册类
        static void *CreateObject(const string &name)
        {
            map<string, CREATE_FUNC>::const_iterator it;
            it = mapCls_.find(name);
            if (it == mapCls_.end())
                return 0;
            else
                return it->second(); //func();
        }
        //在工厂类中注册[注册类]
        static void Register(const string &name, CREATE_FUNC func)
        {
            mapCls_[name] = func;
        }
    private:
        static map<string, CREATE_FUNC> mapCls_;
    };
    
    class Register
    {
    public:
        // 一个trick,初始化Register类,实际上是对工厂类注册[注册类]
        Register(const string &name, CREATE_FUNC func)
        {
            DynObjectFactory::Register(name, func);
        }
    };
    
    // 每次执行一次宏,都会生成一个[注册类],如CircleRegister
    // 同时初始化这个类的静态成员reg_,初始的时候调用了动态工厂的Register方法,完成[注册类]自己的注册
    #define REGISTER_CLASS(class_name) 
    class class_name##Register { 
    public: 
        static void* NewInstance() 
        { 
            return new class_name; 
        } 
    private: 
        static Register reg_; 
    }; 
    Register class_name##Register::reg_(#class_name, class_name##Register::NewInstance)
    
    #endif // _DYN_BASE_H_
    
    //DynBase.cpp
    #include "DynBase.h"
    
    map<string, CREATE_FUNC> DynObjectFactory::mapCls_;
    
    //main.cpp
    #include "Shape.h"
    #include "DynBase.h"
    
    int main(void)
    {
        Shape *ps;
        ps = static_cast<Shape *>(DynObjectFactory::CreateObject("Circle"));
        ps->Draw();
        delete ps;
    
        ps = static_cast<Shape *>(DynObjectFactory::CreateObject("Square"));
        ps->Draw();
        delete ps;
    
        ps = static_cast<Shape *>(DynObjectFactory::CreateObject("Rectangle"));
        ps->Draw();
        delete ps;
    
        return 0;
    }
    
    #CMakeLists.txt
    add_executable(demo DynTest.cpp DynBase.h DynBase.cpp Shape.h Shape.cpp)
    

    3.编译运行

    mkdir build && cd build
    cmake ..
    make
    ./demo
    
    ➜  build ./demo
    Circle
    Square
    Rectangle
    

    4. 小结

    其实从本质上可以自己定义每种Shape对应的Register类,这里使用宏很巧妙地节省了代码,另外,借助每个注册类的静态成员的初始化工厂类,非常巧妙

  • 相关阅读:
    python自学Day01(自学书籍python编程从入门到实践)
    关于要不要转行做程序员的建议
    什么是高并发?
    看了就想收藏的文章链接
    java反射的理解
    Mac执行sh批处理文件出现permission denied
    Error running 'lis [clean]': No valid Maven installation found. Either set the home directory in the configuration dialog or set the M2_HOME environment variable on your system.
    Error running 'Tomcat 8.5.37': Unable to open debugger port (127.0.0.1:55358): java.net.SocketException "socket closed"
    浅谈JS-ES6新特性
    ==和equals的区别
  • 原文地址:https://www.cnblogs.com/fanghao/p/9773914.html
Copyright © 2020-2023  润新知