Error LNK2019 in visual studio: unresolved external symbol public:xxx _ One of the possible reasons for thiscall

Posted by gloeilamp on Fri, 19 Jun 2020 09:30:29 +0200

Recently, I encountered a connection error while writing C + + program. It took a long time to solve this problem, so write down this blog. First, the last error condition:

This is a connection error. To be exact, the connector cannot find the related definition functions or classes or variables in the library or other declaration files. For details, see Microsoft's official statement . The reason for this connection error is probably because you use the template template class, and write the declaration and definition in the header file and the source file respectively. Don't ask me how to know, because I made a mistake like this.

My original intention is to implement a stack class MyStack by myself. Considering that stack allows to add different types of data to it, I used the template class when implementing my MyStack class. My code is as follows:

#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();
};

These are Stack.h files

#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;
}

These are Stack.cpp Documents.

Just when I am happy to think that I have successfully refactored my own stack class and prepared to compile and debug, the top error will be reported. So began to find information, Kangkang in the end is where the problem. To solve this problem, first roll up the code again. After confirming that there is no problem in the code, start to remove the template and directly write all data types as int. at this time, the compilation is passed. So I'm bold to be sure that it's because of template. But why didn't I encounter this problem when I wrote template? (friends who are not familiar with template can be healthy A blog ). After confirming the exception caused by template, I began to recall the implementation mechanism of template in C + + that I learned in school, and turned over the relevant books (so I need to read frequently). Finally realized the wrong point of this problem.

The reason for this error is that when I construct an object or call a method through an object, all methods have not been instantiated, so the linker cannot find the connection target. In other words, we need to tell the compiler which template arguments should be used to instantiate the template. So when the user (main function) tries to call it, the user only knows the declaration (that is, the existence of the declaration in the. h file), but the linker cannot find the definition because it has not been compiled at all.

Template is just the definition of a general-purpose function or class. They use generics to define functions. If you still can't understand it, you can think of template as a macro or inline function, both of which can only be replaced at runtime.
!!!!! The above content is taken from the Chinese version of C++ Primer Plus.

After all this nonsense, how can we solve such problems. The most common way is not to separate the definition of the template function or method from the declaration, but to put them together in the header file, as follows:

#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;
}

In this way, the above problems can be solved, because there is no need to compile the methods in the MyStack class in advance, but to compile and use them when necessary.

Topics: Linker