九九热这里直有精品,1区二区三区在线播放,玖玖爱在线观看资源,国产aⅴ综合网,午夜福利男女,日本亚洲欧美三级,日韩无码黄色导航,内射少妇13区,中文字幕高清网

您身邊的軟件定制專家--9年開發(fā)經(jīng)驗(yàn)為您護(hù)航

18678812288
0531-88887250

c++實(shí)現(xiàn)反射類-山東軟件開發(fā)公司

文章作者:濟(jì)南軟件開發(fā) 時(shí)間:2016年12月20日

在很多程序設(shè)計(jì)中,經(jīng)常會(huì)遇到這樣的需求,即可以通過(guò)類的名字得到對(duì)應(yīng)類型的對(duì)象,尤其是一種數(shù)據(jù)需要很多策略處理的時(shí)候。比如對(duì)于網(wǎng)頁(yè)類型的識(shí)別,一篇網(wǎng)頁(yè)可能是視頻類型、新聞?lì)愋汀D片類型、網(wǎng)站首頁(yè)、百科等很多類型中的一種,網(wǎng)頁(yè)類型對(duì)于搜索引擎來(lái)說(shuō)是非常重要的,計(jì)算rank的時(shí)候網(wǎng)頁(yè)類型往往是一個(gè)非常重要的因子。具體實(shí)現(xiàn)的時(shí)候,網(wǎng)頁(yè)類型識(shí)別的策略可以封裝在類中,這樣一個(gè)策略就可以設(shè)計(jì)成一個(gè)類。但是后期隨著對(duì)網(wǎng)頁(yè)理解的越來(lái)越深入,就會(huì)出現(xiàn)以下兩種情景:

 

需要添加新的網(wǎng)頁(yè)類型,因此需要添加對(duì)應(yīng)的類型識(shí)別類;
 

有些類型已經(jīng)不再需要或者是進(jìn)行了重新劃分,那么需要?jiǎng)h除掉這些類型或者是讓這些類型識(shí)別模塊不再生效。
 

  山東軟件開發(fā)公司這種應(yīng)用場(chǎng)景下,添加或移除網(wǎng)頁(yè)類型識(shí)別模塊時(shí),最好能夠非常方便,并且不會(huì)影響到已有的程序。

 

  一個(gè)比較好的方案是,定義一個(gè)類型識(shí)別的基類PageTypeDetector,每個(gè)類型識(shí)別策略都繼承自這個(gè)基類。比如需要一個(gè)新聞頁(yè)識(shí)別的新策略,那么定義類NewsPageTypeDetector,該類繼承PageTypeDetector。在添加NewsPageTypeDetector到網(wǎng)頁(yè)類型識(shí)別的主程序時(shí),在配置文件中進(jìn)行配置,添加NewsPageTypeDetector類,讓該類生效,而主程序和其他類型識(shí)別策略的程序都不需要進(jìn)行改動(dòng)。另外,如果不再需要圖片網(wǎng)頁(yè)類型識(shí)別,那么就把圖片類型識(shí)別對(duì)應(yīng)的類名直接從配置發(fā)文件中刪除即可。
 

  為了實(shí)現(xiàn)上述目標(biāo),我們需要從類名到類型的映射,可以稱為反射。因?yàn)榕渲梦募械男畔⒃诔绦騼?nèi)部得到的都是純字符串,程序需要根據(jù)字符串生成對(duì)應(yīng)的識(shí)別類。當(dāng)然,這個(gè)在本身已包含反射機(jī)制的程序設(shè)計(jì)語(yǔ)言中很容易實(shí)現(xiàn),比如JAVA,但是由于C++中語(yǔ)言本身不支持這種機(jī)制,因此,需要用其他的方法來(lái)模擬這種機(jī)制。
 

  首先,我們從最簡(jiǎn)單的方式開始,定義一個(gè)工廠方法,該方法負(fù)責(zé)根據(jù)類名生成相應(yīng)類的對(duì)象,函數(shù)定義可以如下 
 

PageTypeDetector* DetectorFactoryCreate(const string& class_name);
 

  生成新聞網(wǎng)頁(yè)類型識(shí)別的類可以如下調(diào)用:

 

PageTypeDetector* news_page_detector = DetectorFactoryCreate("NewsPageTypeDetector");

  DetectorFactoryCreate工廠方法中的實(shí)現(xiàn)邏輯大致是這樣:

 

if (class_name == "NewsDocTypeDetector") {

    return new NewsDocTypeDetector;

} else if (class_name == "...") {

    return new ...;

}

  使用如上工廠方法創(chuàng)建類的方式具有非常明顯的缺陷,每添加或刪除一個(gè)新類,都需要修改工廠方法內(nèi)的程序(添加if判斷或者刪除if判斷,并且需要添加新類的頭文件或者類聲明),當(dāng)然了,因?yàn)槌绦蛴辛诵薷乃跃托枰匦戮幾g(如果很多其他模塊依賴該程序的話,重新編譯也是一筆不小的開銷)。顯然,這種方式雖然簡(jiǎn)單,但是極不易于維護(hù)。

 

  這里,提出一個(gè)使用非常方便并且易于維護(hù)的解決方案,那就是使用宏。雖然c++創(chuàng)始人Bjarne Stroustrup極力反對(duì)使用宏,但是在一些特定的場(chǎng)景中合理的使用宏會(huì)帶來(lái)意想不到的效果。

  山東軟件開發(fā)公司首先,從使用宏最簡(jiǎn)單的一個(gè)實(shí)現(xiàn)開始,目標(biāo)是可以通過(guò)類的名字得到相應(yīng)的對(duì)象,因此應(yīng)該有個(gè)方法類似于如下:

 

1Any GetInstanceByName(const string& class_name);
 

  返回值為Any,因?yàn)椴恢婪祷刂稻烤故鞘裁搭愋停约俣梢苑祷厝魏晤愋?,這里的Any使用的是Boost中的Any。該方法中需要new一個(gè)類型為class_name的對(duì)象返回,那么應(yīng)該如何new該對(duì)象呢?借用上面使用工廠方法的經(jīng)驗(yàn),可以進(jìn)一步使用工廠類,對(duì)于每個(gè)類,都有一個(gè)相應(yīng)的工廠類ObjectFactoryClassName,由該工廠類負(fù)責(zé)生成相應(yīng)的對(duì)象(為什么要使用工廠類?后面再作簡(jiǎn)單介紹)。

 

  有了工廠類,也需要將類名與工廠類對(duì)應(yīng)起來(lái),對(duì)應(yīng)方式可以使用map<string, ObjectFactory*> object_factory_map,object_factory_map負(fù)責(zé)從類名到相應(yīng)工廠類的映射,這樣,就可以通過(guò)類的名字找到對(duì)應(yīng)ObjectFactory,然后使用ObjectFactory生成相應(yīng)的對(duì)象。但是如何將相應(yīng)的工廠類添加到object_factory_map中去呢,我們需要在定義新類的時(shí)候就將對(duì)應(yīng)的工廠類添加到object_factory_map中,這里需要一個(gè)函數(shù)負(fù)責(zé)添加工廠類到object_factory_map中去(為什么需要一個(gè)函數(shù)負(fù)責(zé)?最后作簡(jiǎn)單說(shuō)明)。

 

  負(fù)責(zé)將新類對(duì)應(yīng)的工廠類添加到全局變量object_factory_map的函數(shù)必須在使用object_factory_map之前執(zhí)行。gcc中有一個(gè)關(guān)鍵字__attribute__((constructor)) ,使用該關(guān)鍵字聲明的函數(shù)就可以在main函數(shù)之前執(zhí)行。到現(xiàn)在,程序的結(jié)構(gòu)類似這樣:

 

 

 

 

// 負(fù)責(zé)實(shí)現(xiàn)反射的文件reflector.h:

 

map<string, ObjectFactory*> object_factory_map;

Any GetInstanceByName(const string& name) {

    if (object_factory_map.find(name) != object_factory_map.end()) {

        return object_factory_map[name]->NewInstance();

    }

    return NULL;

}

 

#define REFLECTOR(name) \

class ObjectFactory##name { \ 

public: \

  Any NewInstance() { \

    return Any(new name); \

    } \

}; \

void register_factory_##name() { \

    if (object_factory_map.find(#name) == object_factory_map.end()) { \

      object_factory_map[#name] = new ObjectFactory##name(); \

    } \

} \

__attribute__(constructor)void register_factory##name();

 

 

// 調(diào)用文件test.cc

class TestClass {

public:

  void Out() {

    cout << "i am TestClass" << endl;

  }

};

REFLECTOR(TestClass);

 

// main函數(shù)

int main() {

  Any instance = GetInstanceByName("TestClass");

  TestClass* test_class = instance.any_cast<TestClass>();

  return 0;  

}

 

 

  到這里還有一個(gè)問(wèn)題,全局變量ObjectFactoryMap是不能放在頭文件中的,因?yàn)槿绻鄠€(gè)類包含該頭文件時(shí),就會(huì)出現(xiàn)重復(fù)定義的錯(cuò)誤,是編譯不過(guò)的。因此,將該變量放在其源碼reflector.cc文件中:

 

 

// reflector.h,包含聲明:

extern map<string, ObjectFactory*> object_factory_map;

Any GetInstanceByName(const string& name);

 

// reflector.cc:

map<string, ObjectFactory*> object_factory_map;

Any GetInstanceByName(const string& name) {

    if (object_factory_map.find(name) != object_factory_map.end()) {

        return object_factory_map[name]->NewInstance();

    }

    return NULL;

}

  上述程序編譯能夠通過(guò),但是運(yùn)行時(shí)出錯(cuò),后來(lái)定位到是在使用全局變量object_factory_map時(shí)出錯(cuò),經(jīng)過(guò)調(diào)試了很久,在網(wǎng)上查相應(yīng)的資料也沒找到。經(jīng)過(guò)不停的嘗試,才發(fā)現(xiàn)原來(lái)是全局變量object_factory_map沒有初始化,在仔細(xì)的測(cè)試了以后發(fā)現(xiàn),是__attribute__((constructor))與全局變量類構(gòu)造函數(shù)的執(zhí)行順序的問(wèn)題,一般全局變量是在__attribute__(constructor)前完成初始化的,但是如果__attribute__是在main函數(shù)所在的文件,而全局變量是在其他文件定義的,那么__attribute__(constructor)就會(huì)在全局變量類構(gòu)造函數(shù)前面執(zhí)行,這樣,上面的程序在全局變量類還沒有完成初始化,也就是還沒有執(zhí)行構(gòu)造函數(shù),就在__attribute__(constructor)聲明的函數(shù)中進(jìn)行了使用,因此會(huì)出現(xiàn)問(wèn)題。不過(guò),在執(zhí)行__attribute__時(shí)已經(jīng)看到了全局變量的定義,只是沒有執(zhí)行全局變量的構(gòu)造函數(shù)(這里,如果全局變量不是類,而是普通類型,是沒有問(wèn)題的)。所以,程序的結(jié)構(gòu)還需要進(jìn)一步修改。

 

    山東軟件開發(fā)公司現(xiàn)在解決如何定義和使用全局變量object_factory_map的問(wèn)題。既然我們不能直接使用該變量,那么可以通過(guò)顯示調(diào)用函數(shù)來(lái)返回該變量,如果直接在函數(shù)中new一個(gè)對(duì)象返回的話,那么每次調(diào)用都會(huì)new一個(gè)新的對(duì)象,而我們?nèi)种恍枰粋€(gè)該對(duì)象,這時(shí)該是static出現(xiàn)的時(shí)候了。我們可以這樣定義:

 

// reflector.cc

map<string, ObjectFactory*>& object_factory_map() {

    static map<string, ObjectFactory*>* factory_map = new map<string, ObjectFactory*>;

    return *factory_map;

}

這樣定義還有另外一個(gè)優(yōu)點(diǎn),程序只是在真正需要調(diào)用g_objectfactory_map時(shí)才會(huì)生成相應(yīng)的對(duì)象,而如果程序沒有調(diào)用,也不會(huì)生成對(duì)應(yīng)的對(duì)象。當(dāng)然,在這里new一個(gè)對(duì)象的代價(jià)不大,但是如果new的對(duì)象非常耗時(shí)的話,這種使用函數(shù)中static變量代替全局變量方法的優(yōu)勢(shì)就非常明顯了。到現(xiàn)在反射程序變成如下這樣:

 

 

// 負(fù)責(zé)實(shí)現(xiàn)反射的文件reflector.h:

 

// 工廠類的基類

class ObjectFactory {

 public:

  virtual Any NewInstance() {

    return Any(); 

  }

};

 

map<string, ObjectFactory*>& object_factory_map();

Any GetInstanceByName(const string& name);

 

#define REFLECTOR(name) \

class ObjectFactory##name : public ObjectFactory { \ 

 public: \

  Any NewInstance() { \

    return Any(new name); \

  } \

}; \

void register_factory_##name() { \

    if (object_factory_map().find(#name) == object_factory_map().end()) { \

      object_factory_map()[#name] = new ObjectFactory##name(); \

    } \

} \

__attribute__(constructor)void register_factory##name()

 

 

 

// reflector.cc

 

map<string, ObjectFactory*>& object_factory_map() {

    static map<string, ObjectFactory*>* factory_map = new map<string, ObjectFactory*>;

    return *factory_map;

}

 

Any GetInstanceByName(const string& name) {

    if (object_factory_map().find(name) != object_factory_map().end()) {

        return object_factory_map()[name]->NewInstance();

    }

    return NULL;

}

  到現(xiàn)在接近尾聲了,不過(guò)在很多時(shí)候,我們都是在已有基類的基礎(chǔ)上添加新的類,就好比上述網(wǎng)頁(yè)識(shí)別的程序,各個(gè)識(shí)別策略類都繼承共同的基類,這樣,我們可以進(jìn)一步修改反射程序,將GetInstanceByName放在另外一個(gè)類中,返回的是基類的指針,因此在定義基類時(shí)也需要注冊(cè)一個(gè)宏,如下所示,同時(shí)需要修改objector_factory_map的結(jié)構(gòu)為map<string, map<string, ObjectFactory> >,第一個(gè)key是基類的名字,第二map中的key是生成類的名字,基類宏的定義類似如下:

 

#define REFLECTOR_BASE(base_class) \

class base_class##Reflector { \

 public: \

  static base_class* GetInstanceByName(const string& name) { \

     map<string, ObjectFactory*>& map = object_factory_map()[#base_class]; \

     map<string, ObjectFactory*>::iterator iter = map.find(name); \

     if (iter == map.end()) { \

       return NULL; \

     } \

      Any object = iter->second->NewInstance(); \

      return *(object.any_cast<base_class*>()); \

} \

};

  這里就不再詳細(xì)講修改后的代碼了,有興趣的朋友可以自己實(shí)現(xiàn)。

 

 

 

注:

 

  至于上面為什么需要使用工廠類,而不是直接new一個(gè)對(duì)應(yīng)的對(duì)象返回,原因是直接new是不可以的。例如如下定義 

#define REFLECT(name) \

Any GetInstanceByName(const string& class_name) {

    return Any(new name);

}

  如果是多個(gè)類使用的話,那么就會(huì)出現(xiàn)多個(gè)函數(shù)的定義。如果也借助工廠類的實(shí)現(xiàn),如下實(shí)現(xiàn):

 

 

#define REFLECT(name) \

Any GetInstanceByName##name(const string& class_name) {

    return Any(new name);

}

   這樣是不會(huì)出現(xiàn)重復(fù)定義了,但是這樣在生產(chǎn)新的對(duì)象時(shí)需要指定特定的函數(shù),這不又回到原點(diǎn)了嗎?因此工廠類充當(dāng)?shù)氖莻€(gè)中介的角色,我們可以保存工廠類,然后根據(jù)名稱尋找特定的工廠類來(lái)生成對(duì)應(yīng)的對(duì)象。

 

注:

 

 為什么需要使用函數(shù)添加工廠類?因?yàn)樵诔绦蛑?,全局空間中只能是變量的聲明和定義,而不能是語(yǔ)句,例如:

 

可以這樣寫:

int a = 10;

int main() {}

但是不能這樣寫:

int a;

a = 10;

int main() {}

 

需要注意的知識(shí)點(diǎn):

 

工廠模式;

全局變量的定義需要注意,不能定義在頭文件中(當(dāng)如,如果經(jīng)過(guò)特殊處理,例如使用#ifndef保護(hù)另說(shuō));

Any類型的實(shí)現(xiàn);(準(zhǔn)備寫另外一篇文章來(lái)探討其實(shí)現(xiàn)細(xì)節(jié))

宏的定義以及使用;(基本覆蓋了宏的所有知識(shí))

全局變量構(gòu)造函數(shù)與__attribute__((constructor))的執(zhí)行順序;(調(diào)試了很久)

__attribute__((constructor))的問(wèn)題;(編譯器有關(guān),放在函數(shù)定義前或定義后)

全局空間只能是聲明或者定義,不能是語(yǔ)句;

static在函數(shù)中的使用;

全局變量類的定義與使用。



想要了解更多詳情歡迎來(lái)電咨詢18678812288
登陸網(wǎng)址:m.h6244.cn。
聯(lián)系人:王經(jīng)理。

剑河县| 新宁县| 望都县| 裕民县| 始兴县| 白河县| 大同市| 恩施市| 满洲里市| 塘沽区| 威远县| 仪陇县| 白朗县| 广德县| 环江| 安国市| 贺州市| 泸西县| 宕昌县| 吉水县| 淮安市| 深泽县| 花垣县| 广灵县| 县级市| 全椒县| 毕节市| 寿阳县| 辉南县| 都昌县| 奉贤区| 颍上县| 乌兰浩特市| 敖汉旗| 阿克| 新乐市| 隆化县| 东光县| 中方县| 大理市| 巫溪县|