C++类静态成员必须显示定义,而模板类要写在一个.h文件里,于是出现下面这总情况:
将以上三个文件编译,就会出现some_class<int>::ptr的重定义。
帖子发了以后忽然想到一个解决办法,能部分的解决这个问题,顺便也解决了模板类在.h文件中定义在.cpp文件中实现的问题。但是这个方法:1. 比较繁复;2. 有时不见得能用;3. 确实比较猥琐...
其实问题的中心在于C++编译器把一个.cpp文件编译成一个.obj文件,当你在一个.cpp文件中使用了某个.h文件中声明的内容时,编译器并没有把相应的实现内容取过来,而是留下一个外部引用的记号,再由连接器把引用和实现连接起来。但我们使用模板时,比如说在上面的1.cpp中,我们使用了some_class<int>这个类,当我们编译1.cpp的时候,如果some_class<T>这个模板的实现不在 some_template.h中,编译器就会留下一个记号,等着连接器来连接。然后编译器就去编译别的文件了。而当编译器编译到some_class<T>的实现(比如说在some_template.cpp文件中)时,编译器不知道你再1.cpp文件中使用了some_class<int>,于是它没有生成相应的二进制代码。当然连接器就找不到some_class<int>,进而报错了。
some_class<int>的实现代码没有被生成,那我们就让他生成。唯一的办法是让编译器编译到some_template.cpp时知道some_class<int>被使用了,这很简单,我们使用一次就好了。
具体的办法如下:
在.h文件中只保留some_class的类定义:
在.cpp文件中实现some_class:
在上边的最后一行有一个#include " gen_some_class.h ",我们在这个gen_some_class.h中写入生成模板类实例的代码:
这里的void gen_some_class()是一个谁都不会调用的函数,但是他会让编译器意识到some_class<int>被使用了,some_class<int>::get_ptr()也被使用了,这样编译器就会生成相关代码,连接器就可以将相关代码链接到任何使用它的地方。
事情就这样解决了。
对于比较复杂的情况,比如有100个方法的类,被特化了100个版本,在void gen_some_class()中把100个对象的100个方法逐个调用一边,显然是抽风的做法...另外,对于不能实例化的类(包含纯虚方法),还要费更多的周折。所以基本上这个方法是没有什么应用性的,权当消遣。
//
some_template.h
template < class T >
class some_class
{
public:
static T *ptr;
T* get_ptr(){return ptr;}
} ;
template < class T >
T * some_class < T > ::ptr = NULL;
template < class T >
class some_class
{
public:
static T *ptr;
T* get_ptr(){return ptr;}
} ;
template < class T >
T * some_class < T > ::ptr = NULL;
//
1.cpp
#include " some_template.h "
some_class < int > foo;
#include " some_template.h "
some_class < int > foo;
//
2.cpp
#include " some_template.h "
some_class < int > bar;
#include " some_template.h "
some_class < int > bar;
将以上三个文件编译,就会出现some_class<int>::ptr的重定义。
帖子发了以后忽然想到一个解决办法,能部分的解决这个问题,顺便也解决了模板类在.h文件中定义在.cpp文件中实现的问题。但是这个方法:1. 比较繁复;2. 有时不见得能用;3. 确实比较猥琐...
其实问题的中心在于C++编译器把一个.cpp文件编译成一个.obj文件,当你在一个.cpp文件中使用了某个.h文件中声明的内容时,编译器并没有把相应的实现内容取过来,而是留下一个外部引用的记号,再由连接器把引用和实现连接起来。但我们使用模板时,比如说在上面的1.cpp中,我们使用了some_class<int>这个类,当我们编译1.cpp的时候,如果some_class<T>这个模板的实现不在 some_template.h中,编译器就会留下一个记号,等着连接器来连接。然后编译器就去编译别的文件了。而当编译器编译到some_class<T>的实现(比如说在some_template.cpp文件中)时,编译器不知道你再1.cpp文件中使用了some_class<int>,于是它没有生成相应的二进制代码。当然连接器就找不到some_class<int>,进而报错了。
some_class<int>的实现代码没有被生成,那我们就让他生成。唯一的办法是让编译器编译到some_template.cpp时知道some_class<int>被使用了,这很简单,我们使用一次就好了。
具体的办法如下:
在.h文件中只保留some_class的类定义:
//
some_template.h
template < class T >
class some_class
{
public:
static T *ptr;
T* get_ptr();
} ;
template < class T >
class some_class
{
public:
static T *ptr;
T* get_ptr();
} ;
在.cpp文件中实现some_class:
//
some_template.cpp
template < class T >
T * some_class < T > ::ptr = NULL;
template < class T >
T * some_class < T > ::get_ptr()
{
return ptr;
}
#include " gen_some_class.h "
template < class T >
T * some_class < T > ::ptr = NULL;
template < class T >
T * some_class < T > ::get_ptr()
{
return ptr;
}
#include " gen_some_class.h "
在上边的最后一行有一个#include " gen_some_class.h ",我们在这个gen_some_class.h中写入生成模板类实例的代码:
//
gen_some_class.h
void gen_some_class()
{
some_class<int> sci;
sci.get_ptr();
}
void gen_some_class()
{
some_class<int> sci;
sci.get_ptr();
}
这里的void gen_some_class()是一个谁都不会调用的函数,但是他会让编译器意识到some_class<int>被使用了,some_class<int>::get_ptr()也被使用了,这样编译器就会生成相关代码,连接器就可以将相关代码链接到任何使用它的地方。
事情就这样解决了。
对于比较复杂的情况,比如有100个方法的类,被特化了100个版本,在void gen_some_class()中把100个对象的100个方法逐个调用一边,显然是抽风的做法...另外,对于不能实例化的类(包含纯虚方法),还要费更多的周折。所以基本上这个方法是没有什么应用性的,权当消遣。