visual studio下错误LNK2019:无法解析的外部符号public:xxx _thiscall的可能原因之一

最近在写C++程序的时候碰到了一个连接错误,为了解决这个bug花了很长很长时间,因此写下这篇博客记录一下。首先上个错误情况:
在这里插入图片描述
这是一个连接错误,准确的来说就是连接器无法在库中或其他声明文件找到你在使用中的相关定义函数或类或变量,详细说明可以看下微软的官方声明。出现这个连接错误的原因很可能是因为你使用了template模板类,同时将声明和定义分别写在头文件和源文件中了,别问我怎么知道的,因为我就是这样出错了。

我的初衷是想自己实现一个Stack类——MyStack,考虑到stack是允许往里面添加各种不同类型的数据,因此在实现我的MyStack类的时候就用到了template类,我的代码如下:

#pragma once
#define INIT_SIZE 10
#define RESIZE 5


template<typename T>
class MyStack {
private:
	T* stackElement;
	T* stackBottom;
	T* stackTop;
	int stackSize;
	int stackCapacity;

public:
	MyStack();
	T top()const;
	void pop();
	void push(T element);
	void clear();
	int capacity()const;
	int size()const;
	bool empty()const;
	void reverse();
};

以上是Stack.h文件

#include<iostream>
#include "Stack.h"

template<typename T>
MyStack<T>::MyStack() {
	this->stackCapacity = INIT_SIZE;
	this->stackElement = new T[INIT_SIZE];
	this->stackBottom = this->stackElement;
	this->stackTop = this->stackElement;
	this->stackSize = 0;
}

template<typename T>
T MyStack<T>::top()const {
	if (this->empty()) {
		std::cout << "Stack is empty, nothing can be returned!" << std::endl;
		exit(EXIT_FAILURE);
	}
	return *(this->stackTop - 1);
}

template<typename T>
void MyStack<T>::pop() {
	if (this->empty()) {
		std::cout << "Stack is empty, nothing can be pop out!" << std::endl;
		exit(EXIT_FAILURE);
	}
	this->stackTop--;
	this->stackSize--;
}

template<typename T>
int MyStack<T>::capacity()const {
	return this->stackCapacity;
}

template<typename T>
int MyStack<T>::size()const {
	return this->stackSize;
}

template<typename T>
void MyStack<T>::clear() {
	this->stackTop = this->stackBottom;
	this->stackSize = 0;
}

template<typename T>
void MyStack<T>::push(T element) {
	if (this->stackSize == this->stackCapacity) {
		this->reverse();
	}
	*(this->stackTop) = element;
	this->stackTop++;
	this->stackSize++;
}

template<typename T>
void MyStack<T>::reverse() {
	this->stackCapacity += RESIZE;
	T* newElement = new T[this->stackCapacity];
	int offset = this->stackTop - this->stackBottom;
	this->stackTop = newElement;
	for (int i = 0; i < offset; i++) {
		*(this->stackTop) = *(this->stackBottom);
		this->stackTop++;
		this->stackBottom++;
	}
	this->stackBottom = newElement;
	delete[] this->stackElement;
	this->stackElement = newElement;
}
template<typename T>
bool MyStack<T>::empty()const {
	return this->stackTop == this->stackBottom;
}

以上是Stack.cpp文件。

正当我兴高采烈的认为我成功的重构了属于自己的stack类,准备编译调试的时候,最上面的错误就报出来了。于是开始找资料,康康到底是哪里出了问题。为了解决这个问题,先把代码重新撸了一遍,确认代码没有任何问题之后,开始吧template去掉,直接将所有数据类型都写死为int,这个时候编译通过了。因此我可大胆的确定,就是因为template引起的。但是为什么之前写template的时候都没遇到过这种问题呢?(不熟悉template的朋友可以康康我之前的一篇博客)。在确定了是template引起的异常之后,开始重新回忆一下在学校学的C++中template的实现机制,并翻了翻相关的书(所以说要经常看书)。终于意识到了这个问题的错误点所在。

之所以会产生这样的错误就是,当我构造对象或通过对象调用方法时,所有的方法都还没有实例化,因此链接器找不到连接目标。换句话说,我们要使模板真正的实例化就需要告诉编译器应该要基于哪个模板实参来进行实例化。因此当用户(main函数)尝试去调用的时候,用户只知道有这个声明(即知道.h文件中声明的存在),但是链接器找不到定义,因为压根儿就还没有编译。

template只是通用函数或类的定义,他们是使用泛型来定义函数。如果还是无法理解的话,可以把template想象成是一个宏或者内联函数,这二者都是只有在运行时才对相应的内容做替换。
在这里插入图片描述!!!!!以上内容截取自《C++ Primer Plus中文版》。

讲了这么多废话,该怎么解决这类问题呢。最常用的一种方式就是不要将template的函数或方法的定义与声明分离,而是将二者放在一起,放在头文件中,如下:

#pragma once
#define INIT_SIZE 10
#define RESIZE 5


template<typename T>
class MyStack {
private:
	T* stackElement;
	T* stackBottom;
	T* stackTop;
	int stackSize;
	int stackCapacity;

public:
	MyStack();
	T top()const;
	void pop();
	void push(T element);
	void clear();
	int capacity()const;
	int size()const;
	bool empty()const;
	void reverse();
};


template<typename T>
MyStack<T>::MyStack() {
	this->stackCapacity = INIT_SIZE;
	this->stackElement = new T[INIT_SIZE];
	this->stackBottom = this->stackElement;
	this->stackTop = this->stackElement;
	this->stackSize = 0;
}

template<typename T>
T MyStack<T>::top()const {
	if (this->empty()) {
		std::cout << "Stack is empty, nothing can be returned!" << std::endl;
		exit(EXIT_FAILURE);
	}
	return *(this->stackTop - 1);
}

template<typename T>
void MyStack<T>::pop() {
	if (this->empty()) {
		std::cout << "Stack is empty, nothing can be pop out!" << std::endl;
		exit(EXIT_FAILURE);
	}
	this->stackTop--;
	this->stackSize--;
}

template<typename T>
int MyStack<T>::capacity()const {
	return this->stackCapacity;
}

template<typename T>
int MyStack<T>::size()const {
	return this->stackSize;
}

template<typename T>
void MyStack<T>::clear() {
	this->stackTop = this->stackBottom;
	this->stackSize = 0;
}

template<typename T>
void MyStack<T>::push(T element) {
	if (this->stackSize == this->stackCapacity) {
		this->reverse();
	}
	*(this->stackTop) = element;
	this->stackTop++;
	this->stackSize++;
}

template<typename T>
void MyStack<T>::reverse() {
	this->stackCapacity += RESIZE;
	T* newElement = new T[this->stackCapacity];
	int offset = this->stackTop - this->stackBottom;
	this->stackTop = newElement;
	for (int i = 0; i < offset; i++) {
		*(this->stackTop) = *(this->stackBottom);
		this->stackTop++;
		this->stackBottom++;
	}
	this->stackBottom = newElement;
	delete[] this->stackElement;
	this->stackElement = newElement;
}
template<typename T>
bool MyStack<T>::empty()const {
	return this->stackTop == this->stackBottom;
}

这样就能解决以上问题了,因为这时并不存在对MyStack类中的方法进行提前编译了,而是在需要的时候进行编译和使用。

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值