对象动态创建

还记得之前写过一个代码么?如下:

上面代码是用来根据指定的形状名称来创建相印的形状实例,这里面有很多if...else...语句,如果有新的形状要创建,则里面的代码得不断的进行修改,所以这种设计不是很灵活,所以对象动态创建技术就可以很好的解决此问题。

什么是对象动态创建呢?简单的说它是指可以通过一个字符串创建一个类对象,比如之前我们使用的"Circle"可以创建一个Circle类对象,对于学过JAVA的人来说其实很容易理解,它跟反射技术很类似,所谓反射,它其实是动态的获取类型信息(包括方法和属性):动态的创建对象、动态调用对象中的方法、动态操作对象的属性。而目前只需要观注动态创建对象,这里的目标是:对原有的类不做任何更改,只需要增加一个宏就能够实现动态创建。下面用代码一步步来实现这个目标:

先将之前的Shape类相关的代码拷贝至新的工程中并更改文件名为Shape.h:

并且创建一些其它的文件,需要将代码进行行折分一下:

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 <iostream>
using namespace std;

void Circle::draw() {
    cout<<"Circle::draw() ..."<<endl;
}
Circle::~Circle() {
    cout<<"~Circle() ..."<<endl;
}

void Square::draw() {
    cout<<"Square::draw() ..."<<endl;
}
Square::~Square() {
    cout<<"~Square() ..."<<endl;
}

void Rectangle::draw() {
    cout<<"Rectangle::draw() ..."<<endl;
}
Rectangle::~Rectangle() {
    cout<<"~Rectangle() ..."<<endl;
}

DynTest.cpp【存放测试代码】:

#include "Shape.h"
#include <iostream>
#include <vector>
using namespace std;

//绘制所有的形状
void drawAllShapes(const vector<Shape*>& v) {
    vector<Shape*>::const_iterator it;
    for(it=v.begin();it!=v.end();++it) {
        (*it)->draw();
    }
}

void deleteAllShapes(const vector<Shape*>& v) {
    vector<Shape*>::const_iterator it;
    for(it=v.begin();it!=v.end();++it) {
        delete *it;
    }
}

int main(void) {
    //Shape s;//ERROR,不能实例化抽象类
    vector<Shape*> v;
    /*Shape* ps;
    ps = new Circle;
    v.push_back(ps);
    ps = new Square;
    v.push_back(ps);
    ps = new Rectangle;
    v.push_back(ps);*/

    /*Shape* ps;
    ps = ShapeFactory::createShape("Circle");
    v.push_back(ps);
    ps = ShapeFactory::createShape("Square");
    v.push_back(ps);
    ps = ShapeFactory::createShape("Rectangle");
    v.push_back(ps);*/

    drawAllShapes(v);
    deleteAllShapes(v);

    return 0;
}

以上代码没有什么大的变化,差不多只是将原来一个文件中的代码进行的折分。

接下来编写DynBase.h,之后如果想动态创建对象,只需要包含该头文件既可,也就是代码核心之所在,下面一步步来实现:

实际上就是原先的ShapFactory,只是这里变换了写法,接下来如果哪个类想动态创建对象,可以包含此头文件,并利用宏来注册,伪代码如下:

下面来定义这样的宏,核心中的核心啦:

#ifndef _DYN_BASE_H
#define _DYN_BASE_H
#include <string>
#include <map>

typedef void* (*CREATE_FUNC)();

class DynObjectFactory {
public:
    static void* createObject(const string& name, CREATE_FUNC func) {
        map<string, CREATE_FUNC>::const_iterator it;
        it = mapCls_.find(name);
        if(it != mapCls_.end())
            return 0;
        else
            it->second();
    }

    

private:
    static map<string, CREATE_FUNC> mapCls_;
};

#define REGISTER_CLASS(class_name) \//其中'\'表示换行的意思,在宏声明中换行需要用到它
class class_name##Resgiter { \
public: \
    static void* newInstance() { \
        return new class_name; \
    } \
}; \


#endif //_DYN_BASE_H

接着得想办法将newInstance存入到DynObjectFactory中的mapCls_中,以便创建对象,所以需要在DynObjectFactory类中增加一个注册的方法:

#ifndef _DYN_BASE_H
#define _DYN_BASE_H
#include <string>
#include <map>

typedef void* (*CREATE_FUNC)();

class DynObjectFactory {
public:
    static void* createObject(const string& name, CREATE_FUNC func) {
        map<string, CREATE_FUNC>::const_iterator it;
        it = mapCls_.find(name);
        if(it != mapCls_.end())
            return 0;
        else
            it->second();
    }

    static void register(const string& name, CREATE_FUNC func) {
        mapCls_[name] = func;
    }

private:
    static map<string, CREATE_FUNC> mapCls_;
};

#define REGISTER_CLASS(class_name) \
class class_name##Resgiter { \
public: \
    static void* newInstance() { \
        return new class_name; \
    } \
}; \


#endif //_DYN_BASE_H

下面一步就是想办法来调用register方法,如何做呢?可以用一个技巧:

#ifndef _DYN_BASE_H
#define _DYN_BASE_H
#include <string>
#include <map>

typedef void* (*CREATE_FUNC)();

class DynObjectFactory {
public:
    static void* createObject(const string& name, CREATE_FUNC func) {
        map<string, CREATE_FUNC>::const_iterator it;
        it = mapCls_.find(name);
        if(it != mapCls_.end())
            return 0;
        else
            it->second();
    }

    static void register(const string& name, CREATE_FUNC func) {
        mapCls_[name] = func;
    }

private:
    static map<string, CREATE_FUNC> mapCls_;
};

//创建一个类,利用构造函数去调用DynObjectFactory的register方法达到注册的目的
class Register {
public:
    Register(const string& name, CREATE_FUNC func) {
        DynObjectFactory::register(name, func);
    }
};

#define REGISTER_CLASS(class_name) \
class class_name##Resgiter { \
public: \
    static void* newInstance() { \
        return new class_name; \
    } \
}; \


#endif //_DYN_BASE_H

这时在回到宏定义中,定义一个静态成员变量,如下:

对于静态变量在类体中只是一个声明,我们还需要在类体外面进行定义,所以修改代码如下:

#ifndef _DYN_BASE_H
#define _DYN_BASE_H
#include <string>
#include <map>

typedef void* (*CREATE_FUNC)();

class DynObjectFactory {
public:
    static void* createObject(const string& name, CREATE_FUNC func) {
        map<string, CREATE_FUNC>::const_iterator it;
        it = mapCls_.find(name);
        if(it != mapCls_.end())
            return 0;
        else
            it->second();
    }

    static void register(const string& name, CREATE_FUNC func) {
        mapCls_[name] = func;
    }

private:
    static map<string, CREATE_FUNC> mapCls_;
};

map<string, CREATE_FUNC> DynObjectFactory::mapCls_;

//创建一个类,利用构造函数去调用DynObjectFactory的createObject方法达到注册的目的
class Register {
public:
    Register(const string& name, CREATE_FUNC func) {
        DynObjectFactory::createObject(name, func);
    }
};

#define REGISTER_CLASS(class_name) \
class class_name##Resgiter { \
public: \
    static void* newInstance() { \
        return new class_name; \
    } \
private: \
    static Register reg_; \
}; \

Register class_name##Resgiter::reg_(#class_name, class_name##Resgiter::newInstance);//在实例化Register中主动去调用带两个参数的构造函数,从而达到自动注册


#endif //_DYN_BASE_H

这时编译一下:

报了N多个错误,不要害怕,下面一一来解决它,首先我们忘了包含一个命名空间:

再次编译看一下:

错误一下就减少了很多,继续来解决,先来看"error C2159: 指定了一个以上的存储类",这是哪错了呢?

从代码来看貌似没啥错呀,但是实际上一个隐蔽的错误我们忽略了,那就是默认关键字,register实际上是一个关键字,所以问题很明显啦,更改方法名既可:

再次编译:

还有最后一点错误,接着来解决“error C2014: 预处理器命令必须作为第一个非空白空间启动”,是哪一行呢?

从错误提示来看貌似宏定义中不能以空白空间启动,这行上面确实是空了一行,难道将其删掉才可以,试一下:

再次看下还有没有错误:

成功消除掉所有错误。下面来使用一下这个头文件,看能否动态创建对象?

DynTest.cpp:

#include "Shape.h"
#include "DynBase.h"
#include <iostream>
#include <vector>
using namespace std;

//绘制所有的形状
void drawAllShapes(const vector<Shape*>& v) {
    vector<Shape*>::const_iterator it;
    for(it=v.begin();it!=v.end();++it) {
        (*it)->draw();
    }
}

void deleteAllShapes(const vector<Shape*>& v) {
    vector<Shape*>::const_iterator it;
    for(it=v.begin();it!=v.end();++it) {
        delete *it;
    }
}

int main(void) {
    //Shape s;//ERROR,不能实例化抽象类
    vector<Shape*> v;

    Shape* ps;
    //ps = ShapeFactory::createShape("Circle");
    ps = static_cast<Shape*>(DynObjectFactory::createObject("Circle"));
    v.push_back(ps);
    //ps = ShapeFactory::createShape("Square");
    ps = static_cast<Shape*>(DynObjectFactory::createObject("Square"));
    v.push_back(ps);
    //ps = ShapeFactory::createShape("Rectangle");
    ps = static_cast<Shape*>(DynObjectFactory::createObject("Rectangle"));
    v.push_back(ps);

    drawAllShapes(v);
    deleteAllShapes(v);

    return 0;
}

编译运行:

再次编译:

还是有错误,真的一波三折啊~继续查找原因,没办法:提示是重复定义了,那是哪里重复定义了呢?

而mapCls_对象是在DynBase.h头文件中定义的,是不是有两个文件都包含过了它?

果真是被两个cpp文件包含了,如何解决此问题呢?有两种解决方案:

方案一:将它的定义放到DynBase.cpp文件中,但是还得新建一个文件,我们的目标是不用新建也能编译通过,那就得采用方案二了:

方案二:采用一个扩展的属性来进行修饰,在vc++中可以用"__declspec(selectany)"、在g++中可以用“__attribute((weak))”。

修改代码如下:

有了这个修饰之后,mapCls_既使被多个文件包含也只会定义一次,再次编译看是否解决:

但是还有一个警告,看是哪行代码出的:

代码修改如下:

再次编译:

终于没报错了,下面来运行一下:

呃~~让人崩溃,居然运行出错了,只能硬着头皮来DEBUG看一下,这里直接贴出最终原因,以免写得太哆嗦:

更改为:

最终来运行一下:

下面回过头来看一下动态创建的过程:

而宏只是代码的包含,下面来对其中一个进行代码展开,就拿Circle为例代码展开为:

class CircleResgiter {
public:
    static void* newInstance(){
        return new Circle;
    }
private:
    static Register reg_;
};

Register CircleResgiter::reg_("Circle", CircleResgiter::newInstance())

而这时会调用Register的构造函数,它会去调用DynObjectFactory中的注册方法:

而main函数是在注册之后运行,所以在main函数中就只要去创建对象既可:

这就是整个动态创建的过程,以后如果再有新的形状,只需要定义然后注册一下既可,核心代码完全不要动,扩展性就相当好了,这实际上是组件编程的一个初步。

posted on 2016-07-16 22:25  cexo  阅读(572)  评论(0编辑  收藏  举报

导航