问题描述
我在一个名为 TemplateT 的头文件中的 DLL 中有以下代码:
#pragma once
template<class T>
class __declspec(dllexport) TemplateT {
public:
static int number;
TemplateT(int num) {
number = num;
}
int getNumber() {
return number;
}
};
template<class T>
int TemplateT<T>::number;
#pragma once
#include "TemplateT.h"
#define EXPORTED __declspec(dllexport)
extern EXPORTED TemplateT<int> exported;
在 Exported.cpp 上,我有:
#include "Exporter.h"
TemplateT<int> exported(5);
如您所见,我将静态成员“number”初始化为值 5。 但是当我像这样从另一个项目访问变量时:
#include <iostream>
#include "Exporter.h"
int main()
{
int a = exported.getNumber();
int b = 0;
}
我看到 number 的值为 0。 如果你能向我解释这种行为,我会很高兴。
编辑
两者都用visual studio 2019。dll是用vs19编译的,另一个项目用vs10
two instances of static member
解决方法
[SO]: DLL-Exporting static members of template base class 提供了答案。
您的类(和导出的变量)有 2 个静态成员实例:
- 在 .dll (Exporter.cpp) 中 - 初始化(显式)为 5
- 在 .exe (main.cpp) 中 - 初始化(默认)为 0(这是您实际看到的)
我复制了你的代码,做了一些小改动 - 最重要的是额外的(常规 .dll 导出)文件
导出.h:
#pragma once
#if defined(_WIN32)
# if defined(DLL0_STATIC)
# define DLL0_EXPORT_API
# else
# if defined(DLL0_EXPORTS)
# define DLL0_EXPORT_API __declspec(dllexport)
# else
# define DLL0_EXPORT_API __declspec(dllimport)
# endif
# endif
#else
# define DLL0_EXPORT_API
#endif
以下是调试过程中截取的 2 个屏幕截图:
看number会员地址(红框),2张图不一样(注意实例地址(this)是一样的),所以有证明。
现在,从更大的角度来看,这似乎是一个XY 问题。
我没有看到任何适合这种设计的场景(除了出于好奇目的),因为
静态成员设置为构造函数参数,这意味着新实例将覆盖先前设置的任何内容,因此在某个时间点,该成员将包含传递给最后一个实例构造函数的参数。
#include 指令的真正含义是包含。它有效地将包含的头文件的内容插入到源文件(.c/.cpp)中(递归)。
因此,当您(直接或间接)将 template.h 包含在多个源文件中时,每个源文件都会分配它自己的静态 number
文件。如果将这些源文件编译成一个目标文件,就会出现链接错误。但如果它们被编译成不同的 DLL/EXE,它们将保留自己的静态文件副本。
这类似于全局变量,您通常必须使用 extern
关键字告诉编译器这只是一个声明,而不是定义。