问题:
类模板中成员函数的创建时机:调用阶段。
导致分文件编写时链接不到。
解决:
- 解决方案1:直接包含.cpp源文件。把include "Person.h"变为 include “Person.cpp”。
- 解决方案2:将声明.h文件和实现.cpp文件写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制。
示例:
头文件Person.h中代码:
#pragma once
#include<iostream>
using namespace std;
template<typename T1, typename T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson();
private:
T1 m_Name;
T2 m_Age;
};
源文件Person.cpp中代码:
#include "Person.h"
template<typename T1, typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{
cout << "姓名:" << this->m_Name << ", 年龄:" << this->m_Age << endl;
}
源文件程序入口函数中
#include<iostream>
using namespace std;
#include "Person.h"
void test01()
{
Person<string, int> p("孙悟空", 999);
p.showPerson();
}
int main()
{
test01();
system("pause");
return 0;
}
错误信息:
LNK2019 无法解析的外部符号 "public: __thiscall Person<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>::Person<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int)" (??0?$Person@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z),函数 "void __cdecl test01(void)" (?test01@@YAXXZ) 中引用了该符号 01模板
LNK2019 无法解析的外部符号 "public: void __thiscall Person<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>::showPerson(void)" (?showPerson@?$Person@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@@QAEXXZ),函数 "void __cdecl test01(void)" (?test01@@YAXXZ) 中引用了该符号
LNK1120 2 个无法解析的外部命令
三个错误如上所示。调用阶段,导致分文件编写时链接不到。
解决方案
上面提到了两种:
解决方案一:直接包含源文件。
第一种将程序入口中的头文件
#include "Person.h"
写成
#include "Person.cpp"
问题得到解决。
类模板中的成员函数创建时机:是在调用时才分配内存。
分析:
我们在程序中写包含头文件Person.h文件,编译中,无法生成成员函数。因为编译器没有见到过,所以编译器也不会去找这两个函数,也不会创建,为其分配内存。所以会报错。链接阶段解析不到。
如果我们在程序中直接包含.cpp文件(#include “Person.cpp”),相当于直接让编译器去看cpp中的代码,编译器见过这些代码,在Person.cpp中,又有包含头文件(#include "Person.h"),那么编译器相当于把所有东西都看了一遍。这样就可以正常调用。
这样的做法很少用,因为很少有直接让人看源码的。所以第二种解决方式,才是常用的。
解决方案二:
分开写,由于创建时机的问题。导致无法链接到。
将声明和实现写到同一个文件中,并更改后缀名为.hpp
将.h和.cpp中的内容写到一起,将后缀名改为.hpp文件。只要写hpp文件,别人一看就知这事类模板。约定俗成。
将Person.cpp的代码直接复制到Person.h文件中,然后将Person.h文件重命名为Person.hpp。
用的时候,在程序中直接引用hpp文件。
#include "Person.hpp"
类模板文件Person.hpp文件内容:
#pragma once
#include<iostream>
using namespace std;
template<typename T1, typename T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson();
private:
T1 m_Name;
T2 m_Age;
};
// 类外实现
template<typename T1, typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<typename T1, typename T2>
void Person<T1, T2>::showPerson()
{
cout << "姓名:" << this->m_Name << ", 年龄:" << this->m_Age << endl;
}
此时程序可以正常执行。
总结:
类模板中的成员函数创建时机是在:调用阶段,导致分文件编写时,无法链接到源文件。
为此两种解决方案:
- 第一种:直接引用源文件,
- 第二种:将类模板的声明和实现放在一个文件中,后缀名按照编程规范以.hpp结尾。