C++ 模板特化 偏特化问题

酱油党 2017-11-27 07:43:25
///C++ 模板类中有类自身的静态成员变量

template<typename _Ty>
Class A
{
public:
A()
{
}
~A()
{
}

static A* _instance;
}
template<typename _Ty>
A<_Ty> * A<_Ty>::_instance = NULL;

template<>
Class A<int>
{
public:
A()
{
}
~A()
{
}
static A* _instance;
}
A<int> * A<int>::_instance = NULL;

///这样偏特化静态成员变量初始化后,一旦有两个文件包含该头文件。就会报 A<int>* A<int>::_instance 已经在先编译的cpp文件中定义的链接错误。请问有什么好的解决办法。还望不吝赐教
...全文
865 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
Saleayas 2017-12-04
  • 打赏
  • 举报
回复
__declspec(selectany)
xskxzr 2017-12-03
  • 打赏
  • 举报
回复

A<int> * A<int>::_instance = NULL;
放cpp文件里。
  • 打赏
  • 举报
回复
引用 12 楼 maguiwa 的回复:
[quote=引用 10 楼 zjq9931 的回复:] [quote=引用 9 楼 maguiwa 的回复:] 打了好多,你可以试一下,在另一个.h 或.cpp文件 中 包含a.h文件,会报错的。 我用另一种方式规避了这个问题。但我想知道,按照类的形式进行模板特化 或者 偏特化 类中 静态成员变量的 初始化 应该 怎么写 才能 在多个 文件中进行。打这么多辛苦了!不管怎样先感谢下!
我再两个地方包含了a.h都没有问题,当然我这也是一种规避方式。没有报错。 如果真在你那边报错,我估计你加上一个extern应当就可以了,这个可以实现跨文件的全局变量。当然最好还是仔细再C++ primer这本书里面看看这个用法。[/quote]你只要价格B.cpp包含a.h头文件,就会报错。[/quote] 了解不多,还要多学,我试验出现这个问题了,但我解决掉了。 就是把: template<typename _Ty> A<_Ty> * A<_Ty>::_instance = 0; 放到a.h的最后,去掉了A<int>的那个哈,就可以了。 这样做我也不知道还有没有什么问题, 了解太少,哪里不对的地方,多多指教,学习学习。
酱油党 2017-11-29
  • 打赏
  • 举报
回复
引用 10 楼 zjq9931 的回复:
[quote=引用 9 楼 maguiwa 的回复:] 打了好多,你可以试一下,在另一个.h 或.cpp文件 中 包含a.h文件,会报错的。 我用另一种方式规避了这个问题。但我想知道,按照类的形式进行模板特化 或者 偏特化 类中 静态成员变量的 初始化 应该 怎么写 才能 在多个 文件中进行。打这么多辛苦了!不管怎样先感谢下!
我再两个地方包含了a.h都没有问题,当然我这也是一种规避方式。没有报错。 如果真在你那边报错,我估计你加上一个extern应当就可以了,这个可以实现跨文件的全局变量。当然最好还是仔细再C++ primer这本书里面看看这个用法。[/quote]你只要价格B.cpp包含a.h头文件,就会报错。
酱油党 2017-11-29
  • 打赏
  • 举报
回复
酱油党 2017-11-28
  • 打赏
  • 举报
回复
引用 6 楼 zjq9931 的回复:
我按你的弄怎么没有问题呢? 好吧,你贴上来的代码是有错误的,class写成了Class,其他的还好了。

#pragma once   // 加上这个?当然也可以是宏定义的方式

template<typename _Ty>
class A
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};

template<typename _Ty>
A<_Ty> * A<_Ty>::_instance = NULL;

template<>
class A<int>
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};
A<int> * A<int>::_instance = NULL;
那是你没有用这个类,实际上这个类里是有用到_instance 的函数方法的,只不过我这里没有写。而且只有一个地方包含该头文件也能够使用,但是有多个地方包含该头文件,就会报重复定义的错误
  • 打赏
  • 举报
回复
我按你的弄怎么没有问题呢? 好吧,你贴上来的代码是有错误的,class写成了Class,其他的还好了。

#pragma once   // 加上这个?当然也可以是宏定义的方式

template<typename _Ty>
class A
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};

template<typename _Ty>
A<_Ty> * A<_Ty>::_instance = NULL;

template<>
class A<int>
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};
A<int> * A<int>::_instance = NULL;
酱油党 2017-11-28
  • 打赏
  • 举报
回复
老赵头呢,帮帮忙!~
酱油党 2017-11-28
  • 打赏
  • 举报
回复
引用 3 楼 paschen 的回复:
[quote=引用 2 楼 酱油党的回复:]
[quote=引用 1 楼 paschen 的回复:]
template<typename _Ty>A<_Ty> * A<_Ty>::_instance = 0;

A<int> * A<int>::_instance = 0;

这两句放到源文件里呢
///那样会报无法解析外部符号A<int> * A<int>::_instance 错误。[/quote] 有没包含对应的头文件?[/quote]头文件肯定会包含的
  • 打赏
  • 举报
回复
引用 9 楼 maguiwa 的回复:
打了好多,你可以试一下,在另一个.h 或.cpp文件 中 包含a.h文件,会报错的。 我用另一种方式规避了这个问题。但我想知道,按照类的形式进行模板特化 或者 偏特化 类中 静态成员变量的 初始化 应该 怎么写 才能 在多个 文件中进行。打这么多辛苦了!不管怎样先感谢下!
我再两个地方包含了a.h都没有问题,当然我这也是一种规避方式。没有报错。 如果真在你那边报错,我估计你加上一个extern应当就可以了,这个可以实现跨文件的全局变量。当然最好还是仔细再C++ primer这本书里面看看这个用法。
酱油党 2017-11-28
  • 打赏
  • 举报
回复
引用 8 楼 zjq9931 的回复:
[quote=引用 7 楼 maguiwa 的回复:] 那是你没有用这个类,实际上这个类里是有用到_instance 的函数方法的,只不过我这里没有写。而且只有一个地方包含该头文件也能够使用,但是有多个地方包含该头文件,就会报重复定义的错误
说的很奇怪,我用了这些地方,你看看哪里还没有用到。 a.h

#pragma once

template<typename _Ty>
class A
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};

template<typename _Ty>
A<_Ty> * A<_Ty>::_instance = NULL;

template<>
class A<int>
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};
A<int> * A<int>::_instance = NULL;
Singleton.h

#pragma once
#include "a.h"
#include <iostream>

using namespace std;

class CContrlCenter
{
public:
	static CContrlCenter *GetInstance()
	{
		static CContrlCenter instance;

		A<char> ca;
		cout << "ca " << "befroeSet " << ca._instance << endl;
		ca._instance = &ca;
		cout << "ca " << "afterSet " << ca._instance << endl;
		return &instance;
	}

private:
	CContrlCenter() {};
	
};
Singleton.cpp

/*--------------------------------------------------------------------------------------
一、概念
单例模式:其意图是保证一个雷仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
class CContrlCenter  
{  
//公有的静态方法,来获取该实例  
public:  
static CContrlCenter* GetInstance()  
{  
    if ( m_pInstance == NULL )  //判断是否第一次调用  
        m_pInstance = new CContrlCenter();  
  
    return m_pInstance;  
}  
  
//私有构造函数,防止实例化  
private:  
    CContrlCenter(){};  
  
  
//私有静态指针变量,指向类的唯一实例  
private:  
    static CContrlCenter * m_pInstance; //声明一个静态成员  
};  
  
CContrlCenter* CContrlCenter::m_pInstance = NULL; //定义并初始化静态数据成员  
  
int main()  
{  
    CContrlCenter* ps1 = CContrlCenter::GetInstance();  
    CContrlCenter* ps2 = CContrlCenter::GetInstance();  
    CContrlCenter* ps3 = ps1->GetInstance();  
    CContrlCenter & ps4 = * CContrlCenter :: GetInstance();   
  
    if (ps1 == ps2)  
    {  
        cout<< "ps1 = ps2"<<endl;  
    }  
  
    if (ps1 == ps3)  
    {  
        cout<< "ps1 = ps3"<<endl;  
    }  
  
    if (&ps4 == ps1)  
    {  
        cout<< "ps1 = ps4"<<endl;  
    }  
  
    return 0;  
}  
单例模式通过类本身来管理其唯一实例,唯一的实例是类的一个普通对象,但涉及这个类时,让它只能创建一个实例,并提供对此实例的全局访问。
用户访问唯一实例的方法只有GetInstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。
有一点要注意:一定要加上CContrlCenter* CContrlCenter::m_pInstance = NULL; 这一句,不然的话编译会出错,因为这一句才有变量定义。

二、单例类CContrlCenter有一下特征
它有一个指向唯一实例的静态指针m_Instance,并且是私有的;
它有一个公有的函数,可以获取这个唯一的实例,并且在需要的时候创建该实例;
它的构造函数是私有的,这样就不能从别处创建该类的实例。

三、存在的问题
1.m_pInstance指向的空间什么时候释放呢?
如果在类的西沟行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。
不合理的解决方法:
程序结束时调用GetInstance(),并对返回的指针调用delete操作。这样可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。也就是说释放操作由使用者来管理,而不是类本身来管理,这违背了类的单一原则,这是不合理的。
2.该实例的析构函数什么时候执行?
上面类里面为什么没有析构函数,其实即便你加上析构函数也是可以的,但是这个析构函数不会被执行的。因为你的实例是new出来的,所以只有delete时,才会调用析构函数,但是在哪里调用delete呢?这又回到了上面的问题。
一种妥善的方法:
class CContrlCenter  
{  
public:  
    static CContrlCenter* GetInstance()  
    {  
        static CContrlCenter instance; //静态局部变量  
          
        return &instance;  
    }  
  
private:  
  
    CContrlCenter() {}; //构造函数  
  
};  
--------------------------------------------------------------------------------------*/
#include <iostream>
#include "A.h"
#include "Singleton.h"

using namespace std;


int main()
{
	CContrlCenter *ps1 = CContrlCenter::GetInstance();
	CContrlCenter *ps2 = CContrlCenter::GetInstance();
	CContrlCenter *ps3 = CContrlCenter::GetInstance();
	CContrlCenter &ps4 = *CContrlCenter::GetInstance();

	if (ps1 == ps2)
	{
		cout << "ps1 = ps2" << endl;
	}

	if (ps1 == ps3)
	{
		cout << "ps1 = ps3" << endl;
	}

	if (ps1 == &ps4)
	{
		cout << "ps1 = ps4" << endl;
	}

	A<int> ia;
	cout << "ia " << "beforeSet " << ia._instance << endl;
	ia._instance = &ia;
	cout << "ia " << "afterSet " << ia._instance << endl;

	A<double> da;
	cout << "da " << "beforeSet " << da._instance << endl;
	da._instance = &da;
	cout << "da " << "afterSet " <<da._instance << endl;

	return 0;
}

[/quote]打了好多,你可以试一下,在另一个.h 或.cpp文件 中 包含a.h文件,会报错的。 我用另一种方式规避了这个问题。但我想知道,按照类的形式进行模板特化 或者 偏特化 类中 静态成员变量的 初始化 应该 怎么写 才能 在多个 文件中进行。打这么多辛苦了!不管怎样先感谢下!
  • 打赏
  • 举报
回复
引用 7 楼 maguiwa 的回复:
那是你没有用这个类,实际上这个类里是有用到_instance 的函数方法的,只不过我这里没有写。而且只有一个地方包含该头文件也能够使用,但是有多个地方包含该头文件,就会报重复定义的错误
说的很奇怪,我用了这些地方,你看看哪里还没有用到。 a.h

#pragma once

template<typename _Ty>
class A
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};

template<typename _Ty>
A<_Ty> * A<_Ty>::_instance = NULL;

template<>
class A<int>
{
public:
	A()
	{
	}
	~A()
	{
	}
	static A* _instance;
};
A<int> * A<int>::_instance = NULL;
Singleton.h

#pragma once
#include "a.h"
#include <iostream>

using namespace std;

class CContrlCenter
{
public:
	static CContrlCenter *GetInstance()
	{
		static CContrlCenter instance;

		A<char> ca;
		cout << "ca " << "befroeSet " << ca._instance << endl;
		ca._instance = &ca;
		cout << "ca " << "afterSet " << ca._instance << endl;
		return &instance;
	}

private:
	CContrlCenter() {};
	
};
Singleton.cpp

/*--------------------------------------------------------------------------------------
一、概念
单例模式:其意图是保证一个雷仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
class CContrlCenter  
{  
//公有的静态方法,来获取该实例  
public:  
static CContrlCenter* GetInstance()  
{  
    if ( m_pInstance == NULL )  //判断是否第一次调用  
        m_pInstance = new CContrlCenter();  
  
    return m_pInstance;  
}  
  
//私有构造函数,防止实例化  
private:  
    CContrlCenter(){};  
  
  
//私有静态指针变量,指向类的唯一实例  
private:  
    static CContrlCenter * m_pInstance; //声明一个静态成员  
};  
  
CContrlCenter* CContrlCenter::m_pInstance = NULL; //定义并初始化静态数据成员  
  
int main()  
{  
    CContrlCenter* ps1 = CContrlCenter::GetInstance();  
    CContrlCenter* ps2 = CContrlCenter::GetInstance();  
    CContrlCenter* ps3 = ps1->GetInstance();  
    CContrlCenter & ps4 = * CContrlCenter :: GetInstance();   
  
    if (ps1 == ps2)  
    {  
        cout<< "ps1 = ps2"<<endl;  
    }  
  
    if (ps1 == ps3)  
    {  
        cout<< "ps1 = ps3"<<endl;  
    }  
  
    if (&ps4 == ps1)  
    {  
        cout<< "ps1 = ps4"<<endl;  
    }  
  
    return 0;  
}  
单例模式通过类本身来管理其唯一实例,唯一的实例是类的一个普通对象,但涉及这个类时,让它只能创建一个实例,并提供对此实例的全局访问。
用户访问唯一实例的方法只有GetInstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。
有一点要注意:一定要加上CContrlCenter* CContrlCenter::m_pInstance = NULL; 这一句,不然的话编译会出错,因为这一句才有变量定义。

二、单例类CContrlCenter有一下特征
它有一个指向唯一实例的静态指针m_Instance,并且是私有的;
它有一个公有的函数,可以获取这个唯一的实例,并且在需要的时候创建该实例;
它的构造函数是私有的,这样就不能从别处创建该类的实例。

三、存在的问题
1.m_pInstance指向的空间什么时候释放呢?
如果在类的西沟行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。
不合理的解决方法:
程序结束时调用GetInstance(),并对返回的指针调用delete操作。这样可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。也就是说释放操作由使用者来管理,而不是类本身来管理,这违背了类的单一原则,这是不合理的。
2.该实例的析构函数什么时候执行?
上面类里面为什么没有析构函数,其实即便你加上析构函数也是可以的,但是这个析构函数不会被执行的。因为你的实例是new出来的,所以只有delete时,才会调用析构函数,但是在哪里调用delete呢?这又回到了上面的问题。
一种妥善的方法:
class CContrlCenter  
{  
public:  
    static CContrlCenter* GetInstance()  
    {  
        static CContrlCenter instance; //静态局部变量  
          
        return &instance;  
    }  
  
private:  
  
    CContrlCenter() {}; //构造函数  
  
};  
--------------------------------------------------------------------------------------*/
#include <iostream>
#include "A.h"
#include "Singleton.h"

using namespace std;


int main()
{
	CContrlCenter *ps1 = CContrlCenter::GetInstance();
	CContrlCenter *ps2 = CContrlCenter::GetInstance();
	CContrlCenter *ps3 = CContrlCenter::GetInstance();
	CContrlCenter &ps4 = *CContrlCenter::GetInstance();

	if (ps1 == ps2)
	{
		cout << "ps1 = ps2" << endl;
	}

	if (ps1 == ps3)
	{
		cout << "ps1 = ps3" << endl;
	}

	if (ps1 == &ps4)
	{
		cout << "ps1 = ps4" << endl;
	}

	A<int> ia;
	cout << "ia " << "beforeSet " << ia._instance << endl;
	ia._instance = &ia;
	cout << "ia " << "afterSet " << ia._instance << endl;

	A<double> da;
	cout << "da " << "beforeSet " << da._instance << endl;
	da._instance = &da;
	cout << "da " << "afterSet " <<da._instance << endl;

	return 0;
}

paschen 2017-11-27
  • 打赏
  • 举报
回复
引用 2 楼 酱油党的回复:
[quote=引用 1 楼 paschen 的回复:]
template<typename _Ty>A<_Ty> * A<_Ty>::_instance = 0;

A<int> * A<int>::_instance = 0;

这两句放到源文件里呢
///那样会报无法解析外部符号A<int> * A<int>::_instance 错误。[/quote] 有没包含对应的头文件?
酱油党 2017-11-27
  • 打赏
  • 举报
回复
引用 1 楼 paschen 的回复:
template<typename _Ty>A<_Ty> * A<_Ty>::_instance = 0; A<int> * A<int>::_instance = 0; 这两句放到源文件里呢
///那样会报无法解析外部符号A<int> * A<int>::_instance 错误。
paschen 2017-11-27
  • 打赏
  • 举报
回复
template<typename _Ty>A<_Ty> * A<_Ty>::_instance = 0; A<int> * A<int>::_instance = 0; 这两句放到源文件里呢

5,530

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 模式及实现
社区管理员
  • 模式及实现社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧