免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6128 | 回复: 16
打印 上一主题 下一主题

[C++] 这个模板写的到底有什么问题?为什么老是报重复定义? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-08-23 15:31 |只看该作者 |倒序浏览
这是我接受的一个代码,试图做一个对象工厂,把类名传入对象工厂,让工厂制造对象

工厂类声明
ClassFactory.h
  1. #ifndef __CLASSFACTORY_H__
  2. #define __CLASSFACTORY_H__

  3. #include <string>
  4. #include <map>

  5. typedef void* (*FactoryMethod)(void);
  6. //class Registry;



  7. /*
  8.   * 这个类是用来存储<name, create object function>对,
  9.   * 也就是个对象工厂.
  10.   */
  11. class ClassFactory
  12. {
  13. public:
  14.      static ClassFactory forName(std::string name);
  15.      
  16. public:
  17.      void* newInstance(void);
  18.      
  19. private:
  20.      friend class Registry;
  21.      static bool registerClass(std::string name, FactoryMethod method);
  22.      
  23. private:
  24.      static std::map<std::string, FactoryMethod> registry;   
  25.      std::string name;
  26.      
  27.      explicit ClassFactory(std::string n) : name(n)
  28.      {};
  29. };


  30. class Registry
  31. {
  32. public:
  33.      Registry(const char* name, FactoryMethod method)
  34.      {
  35.          ClassFactory::registerClass(name, method);
  36.      };
  37. };

  38. /*
  39.   *  mixed template, generate new instance for every class.
  40.   */

  41. /*
  42.   * static member, initiliazed before enter into main()
  43.   * call Class::registerClass by ctor of Registry
  44.   */
  45. template <class T, const char name[]>
  46. class Registed
  47. {
  48. public:
  49.         static void* newInstance(void)
  50.         {
  51.                 return new T();
  52.         };

  53. protected:
  54.         Registed()
  55.         {
  56.                 const Registry& dummy = r;
  57.         };

  58. private:
  59.         static const Registry r;
  60. };
  61. template <class T, const char name[]> const Registry
  62.         Registed<T, name>::r = Registry(name, Registed<T, name>::newInstance);      //特化

  63. #define REG_CLASS(CLASS) \
  64. char NameArray[] = #CLASS; \
  65. class CLASS : public Registed<CLASS, NameArray>


  66. #endif // __CLASS_H__
复制代码
ClassFactory.cpp
  1. #include "ClassFactory.h"

  2. std::map<std::string, FactoryMethod> ClassFactory::registry;

  3. ClassFactory ClassFactory::forName(std::string name)
  4. {
  5.         return ClassFactory(name);
  6. };

  7. void* ClassFactory::newInstance(void)
  8. {
  9.         std::map<std::string, FactoryMethod>::const_iterator find;
  10.         find = registry.find(name);
  11.         if (find == registry.end())
  12.                 return NULL;
  13.         else
  14.                 return (find->second)();
  15. };

  16. bool ClassFactory::registerClass(std::string name, FactoryMethod method)
  17. {
  18.         return registry.insert(std::make_pair(name, method)).second;
  19. };
复制代码
希望用工厂类制造的一个示范类
User.h
  1. #ifndef _User_H_
  2. #define _User_H_

  3. #include "cocos2d.h"
  4. #include "ClassFactory.h"

  5. USING_NS_CC;

  6. REG_CLASS(User)
  7. {
  8. public:
  9.         User(){};
  10.         void test();

  11. };

  12. #endif
复制代码
User.cpp
  1. #include "User.h"

  2. void User::test(){
  3.         CCLog("User test");
  4. }
复制代码
现在最古怪的问题在于,如果在任何其它地方引用了User.h,编译器就会报错
error LNK2005: "char * NameArray" (?NameArray@@3PADA) 已经在 xxxxxx.obj 中定义(xxxxx是引用User.h)的源码文件名
,根源在于ClassFactory.h里有这么一段宏
  1. #define REG_CLASS(CLASS) \
  2. char NameArray[] = #CLASS; \
  3. class CLASS : public Registed<CLASS, NameArray>
复制代码
为什么这段宏会造成这个问题?

其次是请解释一下该头文件里这段代码到底干了什么
  1. template <class T, const char name[]> const Registry
  2.         Registed<T, name>::r = Registry(name, Registed<T, name>::newInstance);
复制代码

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
2 [报告]
发表于 2013-08-23 17:35 |只看该作者
回复 1# abcbuzhiming


    char NameArray[] = #CLASS; 是在头文件中定义的,REG_CLASS(User),拉展开来就是char NameArray[] = “User”,这是个定义,所以每个包含User.h的cpp文件都定义了char NameArray[] = “User”,引用几次就定义了几个,这违返了ODR原则,最终导致相同符号多于一次的定义。所以你不能这么去定义NameArray。

至于
  1.     template <class T, const char name[]> const Registry
  2.             Registed<T, name>::r = Registry(name, Registed<T, name>::newInstance);
复制代码
则是模板类静态数据成员的初始化。

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
3 [报告]
发表于 2013-08-23 17:43 |只看该作者
这是个简单的模板类静态数据成员的初始化的代码,你可能容易理解一些。
  1. template<class T> class X {
  2. static T s;
  3. };
  4. template<class T> T X<T>::s = 0;
复制代码

论坛徽章:
0
4 [报告]
发表于 2013-08-23 21:52 |只看该作者
myworkstation 发表于 2013-08-23 17:35
回复 1# abcbuzhiming


用static修饰一下,限定NameArray范围

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
5 [报告]
发表于 2013-08-23 23:06 |只看该作者
回复 4# Aquester
这引出了一个有趣的问题,2003的标准中说这个特性将来要废弃(global static),结果到11标准的时候这个特性反而又被允许了(不再是废弃状态),主要原因是如果废弃将引与有C的严重不兼容以及链接器在处理不具名的命名空间的模板实例化时符号处理会引入额外的负担。

   

论坛徽章:
7
天蝎座
日期:2013-09-28 10:45:42双子座
日期:2013-10-16 16:27:09射手座
日期:2013-10-23 10:21:32处女座
日期:2014-09-17 16:44:332015年亚洲杯之巴林
日期:2015-04-09 17:28:01冥斗士
日期:2015-11-26 16:19:0015-16赛季CBA联赛之山东
日期:2018-03-02 23:59:31
6 [报告]
发表于 2013-08-24 01:34 |只看该作者
  1.     template <class T, const char name[]> const Registry
  2.             Registed<T, name>::r = Registry(name, Registed<T, name>::newInstance);
复制代码
举例
class Abc {
    static const int i;
}

const int Abc::i = 0;   // const 变量需要初始化

-------------------------------------------------------------------
static const Registry r; 是属于类模板的static const数据成员,需要初始化。
因此需要
在类外
const Registry Registed::r = Registry(name, Registed<T, name>::newInstance);
又由于Registed是模板类,所以
template <class T, const char name[]> const Registry Registed<T, name>::r = Registry(name, Registed<T, name>::newInstance);

论坛徽章:
7
天蝎座
日期:2013-09-28 10:45:42双子座
日期:2013-10-16 16:27:09射手座
日期:2013-10-23 10:21:32处女座
日期:2014-09-17 16:44:332015年亚洲杯之巴林
日期:2015-04-09 17:28:01冥斗士
日期:2015-11-26 16:19:0015-16赛季CBA联赛之山东
日期:2018-03-02 23:59:31
7 [报告]
发表于 2013-08-24 01:41 |只看该作者
myworkstation 发表于 2013-08-23 23:06
回复 4# Aquester
这引出了一个有趣的问题,2003的标准中说这个特性将来要废弃(global static),结果到1 ...


你说的c++ 2003标准是指<<c++ primer 4edtion>>中的版本吗?
这本书内是建议使用unname namespace来替global static。

c++11提示了恢复static, 有没有链接看看?

论坛徽章:
0
8 [报告]
发表于 2013-08-24 08:59 |只看该作者
myworkstation 发表于 2013-08-23 17:35
回复 1# abcbuzhiming
挀栀愀爀 NameArray[] = #CLASS; 是在头文件中定义的,REG_CLASS(User),拉展开来就是char NameArray[] = “User”,这是个定义,所以每个包含User.h的cpp文件都定义了char NameArray[] = “User”,引用几次就定义了几个,这违返了ODR原则,最终导致相同符号多于一次的定义。所以你不能这么去定义NameArray。

嗯,看懂了,等于重复定义了一个全局char数组,我的本意是想把类名作为字符串传入类工厂,不能这么定义的话,该怎么写这段宏?

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
9 [报告]
发表于 2013-08-24 13:27 |只看该作者
回复 7# cxytz01


    C++ 03:
[C++03: 7.3.1.1/2]: The use of the static keyword is deprecated when declaring objects in a namespace scope (see annex D); the unnamed-namespace provides a superior alternative.

  C++ 11 proposed and accepted for n3296 :


    ID: FI 6
    Ref: D.2 [depr.static] ¶ Paragraph 1
    Comment: The use of static in namespace scope should not be deprecated. Anonymous namespaces are not a sufficient replacement for the functionality.
    Proposed Resolution: Strike [depr.static] completely.
    Owner: CWG
    Issue: 1012
    Disposition: ACCEPTED


链接:
http://www.open-std.org/jtc1/sc2 ... 2011/n3296.html#FI6

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
10 [报告]
发表于 2013-08-24 13:35 |只看该作者
回复 8# abcbuzhiming


    4楼Aquester说的static是个方法,这样就使得每一个引用了user.h的cpp具有一个NameArray的实例(NameArray此时linkage 是internal),具体的说虽然都可以使用NameArray,但NameArray并不是同一个对象,只是内容相同的不同对象。另外你可以试试用define去定义NameArray。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP