C++使用template模板类,h和cpp分离,编译能通过但是无法运行----报错:无法解析的外部符号

5 篇文章 0 订阅

先贴上报错 :

错误 LNK2019 无法解析的外部符号 “public: __cdecl Person<class std::basic_string<char,struct std::char_traits,class std::allocator >,int>::Person<class std::basic_string<char,struct std::char_traits,class std::allocator >,int>(class std::basic_string<char,struct std::char_traits,class std::allocator >,int)” (??0? P e r s o n @ V ? Person@V? Person@V?basic_string@DU? c h a r t r a i t s @ D @ s t d @ @ V ? char_traits@D@std@@V? chartraits@D@std@@V?allocator@D@2@@std@@H@@QEAA@V? b a s i c s t r i n g @ D U ? basic_string@DU? basicstring@DU?char_traits@D@std@@V?$allocator@D@2@@std@@H@Z),该符号在函数 main 中被引用

再贴上错误代码:

头文件Person.h 如下:

//Person.h
#pragma once
#include <iostream>
#include <string>
using namespace std;

template<class T1,class T2>
class Person
{
public:
	Person(T1 name,T2 age);
	void show();
public:
	T1 mName;
	T2 mAge;
};


源文件Person.cpp 如下:

//Person.cpp
#include "Person.h"
Person::Person(string name, int age)//在此处实现函数
{
	this->mAge = age;
	this->mName = name;
}

void Person::show()
{
	cout << "Name:" << this->mName << " Age:" << this->mAge << endl;
}

main.cpp文件如下:

//main.cpp,这种写法能编译通过,但是无法正常运行
#include <iostream>
#include "Person.h"
int main()
{
	Person<string, int> p("小明", 12);
	p.show();
    std::cout << "Hello World!\n";
}

解决方法:

把 #include “Person.h” 改成 #include “Person.cpp”,即不引入头文件,而引入cpp文件。

并且按照惯例,这时候通常会把文件Person.cpp更名为Person.hpp,再把 #include “Person.cpp” 修改为 #include “Person.hpp”

原因

这涉及到模板的二次编译机制和c++的单独编译机制,可能写得有点乱

模板的二次编译机制

模板会经过两次编译:
第一次对模板本身编译,这一步是在编译到模板代码的时候发生的(即Penson.cpp中发生的);
第二次是在模板调用的时候发生的,此时会根据传入的不同参数类型,生成不同的具体函数,然后再对该具体函数进行编译。(这一步实在main.cpp中,调用)

c++的单独编译机制

c++会对项目里的cpp文件进行单独的编译,例如单独编译Person.cpp生成Person.o,单独编译main.cpp生成main.o,然后把这两个.o文件通过连接器连起来,生成可执行文件。

正常情况下是这样的:当编译到main.cpp时候,编译器无法得知Person.cpp的内容。因此如果main.cpp引用了Person.cpp中的函数show(),编译器就无法编译。此时编译器会默认编译通过,并生成main.o,等Person.cpp也编译完之后,连接器会在Person.o中找到函数show(),并做链接,这样函数就调用起来了。

问题就出在这里,当单独编译Person.cpp时候,仅仅是模板二次编译中的第一次,此时并未生成具体函数,因为并未对模板作调用;而单独编译main.cpp时候,虽然有对模板的调用,但因为单独编译,main.cpp中找不到show()函数的实现,因此会被跳过。最终就导致模板仅有第一次编译,而没有具体函数,所以连接器在.o文件中找不到函数具体show()。

再提一下,错误写法中,引入的是头文件Person.h,编译器并不会对头文件进行编译;而改成Person.cpp时候,再引入时候就会对其进行编译了,此时就发生了模板的第一次编译,再继续往下编译时,就有了show()的调用,所以才能生成具体函数show()

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当我们需要将模板类的定义和实现分离到不同的文件中时,可以将模板类的声明放在一个头文件中,将模板类的实现放在一个cpp文件中。 假设我们有一个模板类`MyClass`的声明: ```c++ // MyClass.h #pragma once template<typename T> class MyClass { public: MyClass(T value); void printValue(); private: T m_value; }; ``` 在模板类的定义中,我们只需要声明构造函数和`printValue`函数的方法,而不需要提供函数的具体实现,因为这些实现将在另一个文件中提供。 现在我们需要在一个cpp文件中提供模板类的实现: ```c++ // MyClass.cpp #include "MyClass.h" #include <iostream> template<typename T> MyClass<T>::MyClass(T value) : m_value(value) {} template<typename T> void MyClass<T>::printValue() { std::cout << "Value: " << m_value << std::endl; } // 显式实例化模板类 template class MyClass<int>; template class MyClass<float>; ``` 在这个文件中,我们提供了模板类的实现,包括构造函数和`printValue`函数的具体代码。此外,我们还需要显式实例化模板类,这将在编译时生成模板类的实例化代码。在这个例子中,我们实例化了`MyClass<int>`和`MyClass<float>`两个类型的模板类。 最后,在需要使用模板类的文件中,只需要包含头文件即可: ```c++ // main.cpp #include "MyClass.h" int main() { MyClass<int> obj(42); obj.printValue(); return 0; } ``` 在这个例子中,我们实例化了一个`MyClass<int>`类型的对象,并调用了它的`printValue`函数。编译器将在编译时生成实例化的代码,并将其链接到最终的可执行文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值