不得不说的一些编程技巧二

作者在 2011-10-28 22:35:34 发布以下内容

         当我第一次看到使用一个字符串描述类型来创建对象时觉得很稀奇,我在猜想,是不是内部使用超多的case,通过判断字符串来创建对应的对象。但是这样的情况是静态的,所有的字符串都已经被预置了,当有一个新的类型出现,那么怎么又神奇的扩展了呢?伪代码如下:

Animal *dog = Animal::Create (“Dog”); // 使用字符串创建Dog类型的Animal实例。
Animal *cat = Animal::Create(“Cat”);  // 同上
          如果Animal::Create不能修改,那么怎么让它能够创建Fish类型的Animal呢?所有的这些铺垫,只是为了让“运行时类”更具光辉。这东西我在MFC中首次看到,并且挖掘了一番,总的来说还是比较简单的,说白了就是一个静态的单向链表。先上代码,然后比划说说:

template<typename t_CreateObjectFun>
class RuntimeClassT
{
public:
    RuntimeClassT(t_CreateObjectFun _fun, Cstr _name)
        : CreateObject(_fun)
        , m_name(_name)
        , m_next(s_first)
    {
        s_first = this;
    }
    static RuntimeClassT* FromClassNameS(Cstr _name)
    {
        for (RuntimeClassT* rc = s_first; rc != 0; rc = rc->m_next)
        {
            if (strcmp(_name, rc->m_name) == 0)
                return rc;
        }
        return Null;
    }

    t_CreateObjectFun CreateObject;

private:
    Cstr                    m_name;
    RuntimeClassT*            m_next;
    static RuntimeClassT*    s_first;
};
template<typename t_CreateObjectFun>
RuntimeClassT<t_CreateObjectFun>* RuntimeClassT<t_CreateObjectFun>::s_first = 0;
          首先说说RuntimeClassT它自身怎么组成一个单向链表。看到那个s_first了吗?这是链表的第一个元素,被赋予了光荣的0。假设RuntimeClassT构造了一个对象A,在构造函数中m_next被赋予s_first,而自身(对象A)被存到了s_first。理解吗?这时s_first就是对象A,而对象Am_next0,没错对象A成了第一个元素。继续,RuntimeClassT再次构造了一个对象Bm_next被赋予s_firsts_first变成对象B。没错,现在对象B成了第一个元素,而它的m_next则指向了对象A,对象Am_next0。这就是单向链表,很简单不是吗。

         明白了RuntimeClassT怎么形成单向链表,那么接下来就看看这单向链表中的元素都含些什么内容。首先是m_name,这是一个c风格的字符串指针,什么作用?好吧,现在看看FromClassNameS这个静态方法。这个方法接受一个c字符串,利用这个c字符串查找在单向链表中有相同内容的RuntimeClassT对象,这个内容就是m_name

         既然从单向链表中找到了需要的RuntimeClassT对象,那么接下来真真的肉戏就是CreateObject了。为了能创建对象CreateObject应该是一个函数指针,或者是一个仿函数。说了这么多,似乎根本还不知道RuntimeClassT有什么作用。因此我写一个示例代码,说明一下:

class Animal
{
public:
    Animal(const String& _name)  : m_name(_name){};
    virtual ~Animal(void) {};
    virtual void Talk(void) = 0;

    // 定义CreateObject的类型。
    typdef Animal* (*Factory_f)(const String&);
    // 实例化RuntimeClassT模板。
    typedef RuntimeClassT<Factory_f>    RuntimeFactory;

    // 实现运行时创建对象的关键代码。
    static Animal* CreateS(Cstr _classname, const String& _name)
{
    // 查找指定的运行时类。
    RuntimeFactory* factory = RuntimeFactory::FromClassName(_classname);
    return factory ? factory->CreateObject(_name) : Null;
}
private:
    String&  m_name;
};

class Dog : public Animal
{
public:
    Dog(const String& _name) : Animal(_name) {};
    void Talk(void) { … };

    // 实现CreateObject的方法。
    static Animal* DogFactoryS(const String& _name)
{
    return new Dog(_name);
}
// 这里的目的是加入名为Dog的运行时类到链表。
    static RuntimeFactory& GetRuntimeFactoryS(void)
{
    static RuntimeFactory factory(DogFactoryS, “Dog”);
    return factory;
}
};
// 实例化静态对象。
namespace { Animal::RuntimeFactory& dogfactory = Dog::GetRuntimeFactoryS(); };

class Cat : public Animal
{
public:
    Cat (const String& _name) : Animal(_name) {};
    void Talk(void) { … };

    // 实现CreateObject的方法。
    static Animal* CatFactoryS(const String& _name)
{
    return new Cat(_name);
}
// 这里的目的是加入名为Cat的运行时类到链表。
    static RuntimeFactory& GetRuntimeFactoryS(void)
{
    static RuntimeFactory factory(CatFactoryS, “Cat”);
    return factory;
}
};
// 实例化静态对象。
namespace { Animal::RuntimeFactory& catfactory = Cat::GetRuntimeFactoryS(); };

class Fish : public Animal
{
public:
    Fish (const String& _name) : Animal(_name) {};
    void Talk(void) { … };

    // 实现CreateObject的方法。
    static Animal* Fish FactoryS(const String& _name)
{
    return new Fish (_name);
}
// 这里的目的是加入名为Fish的运行时类到链表。
    static RuntimeFactory& GetRuntimeFactoryS(void)
{
    static RuntimeFactory factory(Fish FactoryS, “Fish”);
    return factory;
}
};
// 实例化静态对象。
namespace { Animal::RuntimeFactory& fishfactory = Fish::GetRuntimeFactoryS(); };
          我是个懒人只写了Dog类,后面的基本上是复制和粘贴,所以错误难免。现在我们可以这样做:

Animal* dog = Animal::CreateS(“Dog”, “X”);
Animal* cat = Animal::CreateS(“Cat”, “Y”);
Animal* fish = Animal::CreateS(“Fish”, “Z”);
          如果你乐意,还可以再写个Fox类,AnimalCreateS方法不用修改即可使用,很棒不是吗?

          上面我把GetRuntimeFactoryS都放到匿名命名空间中调用了一次,这目的是为了实例化函数中的静态对象。为什么不直接定义一个静态数据成员呢?我太累了,留给大家自己试验吧。

 

c++ | 阅读 1389 次
文章评论,共1条
O123
2011-11-02 09:29
1
赞!RuntimeClassT用哈希表实现应该比单链表好些。
游客请输入验证码
浏览14402次