动态2:由类名创建对象

原创 2004年09月19日 02:36:00

前言:同上文一样,本文源于对另一位朋友的问题的解答
(参见帖子http://community.csdn.net/Expert/topic/3202/3202729.xml?temp=5.602664E-02

C++不是动态语言,所以没法从语言机制上实现类的动态创建,但这样的需求却有可能存在,一个类似的例子便是MFC中CWnd类的Create方法,其第一个参数为Window Class的名字,这就允许用户通过class的名字来创建相应的窗口。

要想实现这一点,必须有一个“管理中心”,用于登记类的名字,并且通过名字能够调用某个方法来创建相应的类。结合类工厂的设计思想,这里我们让一套继承体系中的基类作为“管理中心”,由它来维护所有派生类的必要信息,包括类名和工厂函数,这二者必须建立起映射关系,map是不错的选择。定义了一个派生类后,它就自动向基类进行注册,可是如何实现自动呢?先看看程序吧:

// 为编写方便,下面的代码都位于.cpp文件中
#ifdef _MSC_VER
#pragma warning(disable: 4786)
#endif
#include <iostream>
#include <map>
using namespace std;

class Base
{
public:
    virtual void Print() // 测试用
    {
        cout << "This is Base" << endl;
    }
protected:
    typedef Base* (*ClassGen)(); // 声明函数指针
    static void Register(const char* class_name, ClassGen class_gen) // 注册函数
    {
        class_set.insert(map<const char*, ClassGen>::value_type(class_name, class_gen));
    }
public:
    static Base* Create(const char* class_name) // 工厂函数
    {
        map<const char*, ClassGen>::iterator iter;
        if((iter = class_set.find(class_name)) != class_set.end())
        {
            return ((*iter).second)();
        }
        return NULL;
    } 
protected:
    static map<const char*, ClassGen> class_set; // 存储子类信息
};

map<const char*, Base::ClassGen> Base::class_set; // 静态成员变量定义

class Derived : public Base
{
public:
    struct DerivedRegister // 辅助类,用于注册 
    {
        DerivedRegister()
        { // 注册子类,虽然map能保证唯一,但仍只注册一次
            static bool bRegistered = false;
            if(!bRegistered)
            {
                Base::Register("Derived", Derived::Create); // 注册子类信息 
                bRegistered = true;
            }
        }    
    };
    static Base* Create() // 工厂函数
    {
        return new Derived;
    }
public:
    virtual void Print() // 测试用
    {
        cout << "This is Derived" << endl;
    }
};

static Derived::DerivedRegister Derived_for_registering; // 没有其它机制能够保证在全局空间调用注册函数,
// 不得已,定义一个全局变量来完成这项光荣的任务,看起来有点龌龊

int main()
{
    Base* pDerived = Base::Create("Derived"); // 类名可以动态输入
    if(pDerived) pDerived->Print(); // 创建成功调用虚函数
    else cout << "Create error" << endl;
    system("pause");
    return 0;
}

上面这样实现,有的朋友可能觉得使用起来很麻烦,实际上我们用宏定义来改造一下就会非常漂亮,代码呆会儿就会看到。还是说说自动注册功能吧,首先,为什么要实现自动注册呢?这是为了最大程度上隐藏实现,实际上采用手动注册也可以,只不过我们得在主函数里一一调用每个类的注册函数。那我们是如何实现自动注册的呢?方法是将注册代码放到一个辅助类的构造函数中,然后定义一个该类的静态全局变量,这样构造函数便被调用了,:),缺点是多了一个额外的对象,除了用于注册外,它什么用也没有(最初我没有采用辅助类,而是直接在派生类的构造函数中注册,然后定义派生类的全局变量,这样明显会浪费空间,采用辅助类便将额外开销降低到了最小)。

下面让我们瞧瞧用宏改造后的模样:

#ifdef _MSC_VER
#pragma warning(disable: 4786)
#endif
#include <iostream>
#include <map>
using namespace std;

// 用于声明具有动态创建功能的基类 
#define DECLARE_DYNCRT_BASE(base) /
public: /
    typedef base* (*ClassGen)(); /
    static void Register(const char* class_name, ClassGen class_gen) /
    { /
        class_set.insert(map<const char*, ClassGen>::value_type(class_name, class_gen)); /
    } /
public: /
    static base* Create(const char* class_name) /
    { /
        map<const char*, ClassGen>::iterator iter; /
        if((iter = class_set.find(class_name)) != class_set.end()) /
        { /
            return ((*iter).second)(); /
        } /
        return NULL; /
    } /
protected: /
    static map<const char*, ClassGen> class_set; 

// 用于实现基类 
#define IMPLEMENT_DYNCRT_BASE(base) /
map<const char*, base::ClassGen> base::class_set;

// 用于声明一个能够被动态创建的类 
#define DECLARE_DYNCRT_CLASS(derived, base) /
public: /
    struct derived##Register /
    { /
        derived##Register() /
        { /
            static bool bRegistered = false; /
            if(!bRegistered) /
            { /
                base::Register(#derived, Create); /
                bRegistered = true; /
            } /
        } /
    }; /
    static base* Create() /
    { /
        return new derived; /
    }

// 用于实现一个能够被动态创建的类  
#define IMPLEMENT_DYNCRT_CLASS(derived) /
static derived::derived##Register derived##_for_registering; 

// 测试
class Base
{
    DECLARE_DYNCRT_BASE(Base) // 声明动态基类 
    DECLARE_DYNCRT_CLASS(Base, Base) // 基类自己也可以动态创建 
public:
    virtual void Print() 
    {
        cout << "This is Base" << endl;
    }
};

IMPLEMENT_DYNCRT_BASE(Base) // 实现动态基类 
IMPLEMENT_DYNCRT_CLASS(Base) // 实现动态类

class Derived : public Base
{
    DECLARE_DYNCRT_CLASS(Derived, Base) // 声明动态类 
public:
    virtual void Print() 
    {
        cout << "This is Derived" << endl;
    }
};

IMPLEMENT_DYNCRT_CLASS(Derived) // 实现动态类

int main()
{
    Base* pBase = Base::Create("Base"); // 类名可以动态输入
    if(pBase) pBase->Print(); // 创建成功调用虚函数
    else cout << "Create Base error" << endl;
    
    Base* pDerived = Base::Create("Derived"); // 类名可以动态输入
    if(pDerived) pDerived->Print(); // 创建成功调用虚函数
    else cout << "Create Derived error" << endl;
    
    system("pause");
    return 0;
}

上面的宏定义可以重复利用(是不是有点似曾相识,是的,MFC中在对序列化和动态创建等支持时用到了类似的宏),在使用的时候,只需简单的使用这四个宏,即可实现动态创建,感觉还不错吧,不过提醒一点,其中的两个IMPLEMENT宏都应该放在.cpp文件中。

这种方法还是存在一些缺点的,不过本文主要是提供一种思路,可以根据具体的情况再作相应变动。

(freefalcon于2004.09.19)

c#通过类名动态创建对象方法

using System;  using System.Reflection;  Assembly assem = Assembly.Load("dll名称");  Type type=...
  • fengzheng22
  • fengzheng22
  • 2013年12月11日 16:45
  • 599

C++实现反射(根据类名动态创建对象)

和网上大多数实现一样,本文也采用工厂方法来实现对象的动态创建,唯一不同的在于,本文的实现优化了类的注册,使用起来更为简单。废话不多说,直接上代码(这也没什么可说的,看代码更加直观)。 DynamicF...
  • heyuhang112
  • heyuhang112
  • 2016年06月21日 18:38
  • 3050

由类名创建对象(转)

C++不是动态语言,所以没法从语言机制上实现类的动态创建,但这样的需求却有可能存在,一个类似的例子便是MFC 中CWnd 类的Create 方法,其第一个参数为Window Class 的名字...
  • cpytiger
  • cpytiger
  • 2011年05月20日 13:36
  • 390

由类名创建对象

#pragma once#include #include using namespace std;// 用于声明具有动态创建功能的基类 #define DECLARE_DYNCRT_BASE(bas...
  • kingmax26
  • kingmax26
  • 2011年04月06日 17:24
  • 649

实现用类名来动态创建类

突发奇想写了这个非常简单,但是很有用的东西:需要 boost 库支持文件名:  Runtimeclass.hpp #include...
  • jadedrip
  • jadedrip
  • 2006年08月16日 23:42
  • 1586

Swift通过类名创建对象

OC中可以使用NSClassFromString将字符串直接转换为类名,在Swift中利用NSClassFromString不出意外结果都为nil,因为Swift中根据字符串转换的方法需要加上Your...
  • zww1984774346
  • zww1984774346
  • 2016年06月18日 16:23
  • 2880

VCL动态创建对象-根据类名创建对象

在开发的过程中我们有时需要在运行创建某个类或都它的子类的对象,而在设计时我们并不知道到底要创建哪个具体子类的的实例,这就需要用到动态创建的方法。        Delphi可以根据类的名字来创建对,...
  • AIIB69
  • AIIB69
  • 2018年01月02日 22:34
  • 44

根据字符串类名动态创建对象

老板很无理的要求,硬是让程序能够根据给定的字符串类名来创建相应的对象,哎,为此折腾了几天!////////////////////////reflect.h文件#ifndef REFLECT_H#de...
  • yysdsyl
  • yysdsyl
  • 2008年07月03日 19:49
  • 1618

Swift通过类名动态创建对象的方式

原文出处:Swift通过类名动态创建对象的方式前言最近一些朋友问到我在Swift中如何通过类字符串名称的方式创建类实例的问题,起初以为与Objective-C的差不多吧,事实上还是有很大的差别的。下面...
  • woaifen3344
  • woaifen3344
  • 2015年10月16日 16:00
  • 9604

Swift3.1-根据类名动态创建类

什么是命名空间(namespace) 通常来说,命名空间是唯一识别的一套名字,这样当对象来自不同的地方但是名字相同的时候就不会含糊不清了。 为什么需要命名空间(namespace) Object...
  • longshihua
  • longshihua
  • 2017年03月09日 20:28
  • 1576
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:动态2:由类名创建对象
举报原因:
原因补充:

(最多只允许输入30个字)