我想先说这个问题,这是我第一次提出我已经提出的问题;如果我遗漏了你们需要的东西,请告诉我。
过去几天我一直在梳理我在互联网上找到的所有内容,而我似乎无法弄清楚我需要做些什么来解决这些错误。这些文件都是同一个项目的一部分。我也在使用QT。我也意识到我的代码中可能存在逻辑错误。在我运行任何测试之前,我遇到了这些链接器错误。
错误:
error LNK2001: unresolved external symbol "private: static class RadixTree<class ConsoleWidget::FunctionWrapper> ConsoleWidget::mCommands" (?mCommands@ConsoleWidget@@0V?$RadixTree@VFunctionWrapper@ConsoleWidget@@@@A)
error LNK2019: unresolved external symbol "public: bool __cdecl RadixTree<class ConsoleWidget::FunctionWrapper>::Insert(char const *,class ConsoleWidget::FunctionWrapper *)" (?Insert@?$RadixTree@VFunctionWrapper@ConsoleWidget@@@@QEAA_NPEBDPEAVFunctionWrapper@ConsoleWidget@@@Z) referenced in function "public: __cdecl ConsoleWidget::ConsoleWidget(void)" (??0ConsoleWidget@@QEAA@XZ)
error LNK2019: unresolved external symbol "public: class ConsoleWidget::FunctionWrapper * __cdecl RadixTree<class ConsoleWidget::FunctionWrapper>::Contains(char *)" (?Contains@?$RadixTree@VFunctionWrapper@ConsoleWidget@@@@QEAAPEAVFunctionWrapper@ConsoleWidget@@PEAD@Z) referenced in function "public: __cdecl ConsoleWidget::ConsoleWidget(void)" (??0ConsoleWidget@@QEAA@XZ)
对于代码墙感到抱歉,我想我应该只包含相关文件中的所有内容。 = /
RadixTree.h
#ifndef RADIX_TREE
#define RADIX_TREE
#define numStorableChars 26
template<class Value> class RadixTree{
class Node{
public:
char* mStringPart;
Node* mNextNodes[26];
Value* mValue;
Node(){
mStringPart = 0;
mValue = 0;
mNextNodes = 0;
}
};
public:
RadixTree();
~RadixTree();
Value* Contains(char* key);
bool Insert(const char* key, Value* value);
private:
Node* mRoot;
void deleteTree(Node* node);
};
#endif
RadixTree.cpp
#include <RadixTree.h>
template<class Value> RadixTree<Value>::RadixTree(){ mRoot = new Node(); }
template<class Value> RadixTree<Value>::~RadixTree(){ deleteTree(mRoot); }
template<class Value> void RadixTree<Value>::deleteTree(Node* node){
delete node->mStringPart;
delete node->mValue;
if(node->mNextNodes){
for(int i = 0; i < numStorableChars; i++){
if(node->mNextNodes[i])
deleteTree(node->mNextNodes[i]);
}
delete node->mNextNodes;
}
delete node;
}
template<class Value> Value* RadixTree<Value>::Contains(char* key){
Node* temp = mRoot;
while(*key != '\0'){
//////////////////////////////////////////////////////////////////////////
//Check the index of the Node array
if(*key < 97 || *key > 122) return 0; //check for valid character
if(!temp->mNextNodes[*key - 97]) return 0; //check that node exists
temp = temp->mNextNodes[*key - 97];
key++;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//Check the string part the node represents
if(temp->mStringPart){
char* stringIter = temp->mStringPart;
while(*key != '\0' && *stringIter != '\0'){
if(*key != *stringIter) return 0; //Not in tree
key++;
stringIter++;
}
if(*key == '\0' && *stringIter == '\0') break; //Found what we were looking for
if(*stringIter != '\0') return 0; //Not in tree
}
//////////////////////////////////////////////////////////////////////////
}
return temp->mValue;
}
template<class Value> bool RadixTree<Value>::Insert(const char* key, Value* value){
Node* temp = mRoot;
Node* prevTemp = temp;
//check for valid characters
char* keyIter = key;
while(*keyIter != '\0'){
if(*keyIter < 97 || *keyIter > 122) return false;
keyIter++;
}
keyIter = key;
char* prevKeyIter = 0;
while(true){
//////////////////////////////////////////////////////////////////////////
//Does the way we want to go have a node?
if(!temp->mNextNodes[*keyIter - 97]){ //if not, make one and we're done
temp->mNextNodes[*keyIter - 97] = new Node[26];
temp->mNextNodes[*keyIter - 97]->mValue = value;
if(*(keyIter++) != '\0') strncpy(temp->mNextNodes[*keyIter - 97]->mStringPart, keyIter, strlen(keyIter));
return true;
}
//////////////////////////////////////////////////////////////////////////
prevTemp = temp;
temp = temp->mNextNodes[*keyIter - 97];
prevKeyIter = keyIter;
keyIter++;
//////////////////////////////////////////////////////////////////////////
//Check the string part the node represents
if(temp->mStringPart){
char* stringPart = temp->mStringPart;
//char* prevStringPart = stringPart;
while(true){
if(*keyIter == '\0' && *stringPart == '\0'){//We found the node we were looking for
if(temp->mValue) return false;//There is something already here; we are done
else { //We can insert the value and be done
temp->mValue = value;
return true;
}
} else if(*keyIter != '\0' && *stringPart == '\0') break; //We need to go deeper
else if(*keyIter == '\0' && *stringPart != '\0'){//We need to split strings and insert a node
Node* newNode = new Node();
newNode->mValue = value;
if(stringPart - temp->mStringPart != 0)
strncpy(newNode->mStringPart, temp->mStringPart, stringPart - temp->mStringPart);
newNode->mNextNodes[*stringPart] = temp;
if(*(stringPart + 1) == '\0'){//Is there anything left after cutting up the string
delete temp->mStringPart;
temp->mStringPart = 0;
} else {
char* tempChar = temp->mStringPart;
strncpy(temp->mStringPart, stringPart + 1, strlen(stringPart + 1));
delete tempChar;
}
prevTemp->mNextNodes[*prevKeyIter] = newNode;
} else {//(*keyIter != '\0' && *stringPart != '\0') //Keep checking the strings
if(*keyIter != *stringPart){//We need to split strings and insert a node
Node* newNode = new Node();
//newNode->mValue = value;
if(stringPart - temp->mStringPart != 0)
strncpy(newNode->mStringPart, temp->mStringPart, stringPart - temp->mStringPart);
newNode->mNextNodes[*stringPart] = temp;
if(*(stringPart + 1) == '\0'){//Is there anything left after cutting up the string
delete temp->mStringPart;
temp->mStringPart = 0;
} else {
char* tempChar = temp->mStringPart;
strncpy(temp->mStringPart, stringPart + 1, strlen(stringPart + 1));
delete tempChar;
}
prevTemp->mNextNodes[*prevKeyIter] = newNode;
temp = newNode;
newNode = new Node();
newNode->mValue = value;
if(*(keyIter + 1) != '\0'){
strncpy(newNode->mStringPart, keyIter + 1, strlen(keyIter + 1));
}
temp->mNextNodes[*keyIter] = newNode;
} else {//Go around for another loop
prevKeyIter = keyIter;
keyIter++;
stringPart++;
}
}
}
} else {
if(*keyIter == '\0'){//We found the node we were looking for
if(temp->mValue) return false;//There is something already here; we are done
else { //We can insert the value and be done
temp->mValue = value;
return true;
}
}
}
//////////////////////////////////////////////////////////////////////////
}
return false;
}
ConsoleWidget.hqt(我的项目设置为在扩展名为.hqt的头文件上运行QTs MOC)
#ifndef CONSOLE_WIDGET
#define CONSOLE_WIDGET
#include <QtWidgets\qwidget.h>
#include <QtWidgets\QLineEdit.h>
#include <QtWidgets\QTextEdit.h>
#include <QtWidgets\qstyleoption.h>
#include <QtWidgets\qsizegrip.h>
#include <QtWidgets\qgridlayout.h>
#include <QtWidgets\qscrollbar.h>
#include <QtGui\qpainter.h>
//#include <QtCore\qhash.h>
#include <RadixTree.h>
//#include <Intermediary.hqt>
/*
Necessary to get around issues with static functions emiting signals.
*/
class Intermediary: public QObject{
Q_OBJECT
public:
void emitMessage(QString message){ emit displayUpdated(message); };
signals:
void displayUpdated(QString message);
};
class ConsoleWidget:public QWidget{
Q_OBJECT
enum CommandFlag{
None,
ArgumentsHelp,
DetailedHelp
};
enum MessageType{
Normal,
UserInput,
Error
};
class FunctionWrapper{
public:
FunctionWrapper(void(*function)(QStringList, ConsoleWidget::CommandFlag)){ mFunction = function; }
void(*mFunction)(QStringList, ConsoleWidget::CommandFlag);
};
public:
ConsoleWidget();
void setParent(QWidget *parent);
void runCommand(QString commandString, MessageType type);
static void addCommand(QString command, void(*commandFunction)(QStringList, CommandFlag));
static void addMessage(QString message, MessageType type);
protected:
void paintEvent(QPaintEvent* event);
private:
QLineEdit* mInputWidget;
QSizeGrip* mResizeGrip;
QTextEdit* mConsoleDisplay;
static Intermediary mIntermediary;
static QString mConsoleHistory;
static RadixTree<FunctionWrapper> mCommands;
static void helpCommand(QStringList commandElements, CommandFlag flag);
private slots:
void userCommand();
void updateConsoleDisplay(QString message);
};
#endif
ConsoleWidget.cpp
#include <ConsoleWidget.hqt>
//TODO help functionality
//previously typed commands
Intermediary ConsoleWidget::mIntermediary;
QString ConsoleWidget::mConsoleHistory;
//RadixTree<struct FunctionWrapper> ConsoleWidget::mCommands;
ConsoleWidget::ConsoleWidget(){
connect(&mIntermediary, SIGNAL(displayUpdated(QString)), this, SLOT(updateConsoleDisplay(QString)));
mInputWidget = new QLineEdit("", this);
connect(mInputWidget, SIGNAL(returnPressed()), this, SLOT(userCommand()));
mConsoleDisplay = new QTextEdit("", this);
mConsoleDisplay->setHtml(mConsoleHistory);
mConsoleDisplay->setReadOnly(true);
mResizeGrip = new QSizeGrip(0);
if(!mCommands.Contains("help")) mCommands.Insert("help", new FunctionWrapper(helpCommand));
QWidget* verticalDivider = new QWidget(this);
verticalDivider->setFixedWidth(1);
verticalDivider->setStyleSheet("background-color: rgb(82, 82, 82);");
QWidget* horizontalDivider = new QWidget(this);
horizontalDivider->setFixedHeight(1);
horizontalDivider->setStyleSheet("background-color: rgb(82, 82, 82);");
QWidget* spacer = new QWidget(this);
spacer->setFixedWidth(4);
spacer->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
QGridLayout* consoleLayout = new QGridLayout();
consoleLayout->setMargin(0);
consoleLayout->setSpacing(0);
consoleLayout->addWidget(mConsoleDisplay, 0, 0, 1, 3);
consoleLayout->addWidget(spacer, 0, 3, 1, 1);
consoleLayout->addWidget(horizontalDivider, 1, 0, 1, 4);
consoleLayout->addWidget(mInputWidget, 2, 0, 1, 1);
consoleLayout->addWidget(verticalDivider, 2, 1, 1, 1);
consoleLayout->addWidget(mResizeGrip, 2, 2, 1, 2);
setLayout(consoleLayout);
}
void ConsoleWidget::addCommand(QString command, void(*commandFunction)(QStringList, CommandFlag)){
command = command.toLower();
if(mCommands.Contains(command.toUtf8().data())){
addMessage("\"" + command + "\" can not be added to ConsoleWidget because it's already here!", Error);
return;
}
mCommands.Insert(command.toUtf8().data(), new FunctionWrapper(commandFunction));
}
void ConsoleWidget::addMessage(QString message, MessageType type){
switch(type){
case Normal:
mConsoleHistory.append(message);
emit mIntermediary.emitMessage(message);
break;
case UserInput:
mConsoleHistory.append("<font color=#ffffff>" + message + "</font><br>");
emit mIntermediary.emitMessage("<font color=#ffffff>" + message + "</font><br>");
break;
case Error:
mConsoleHistory.append("<font color=#e60000>Error: " + message + "</font><br>");
emit mIntermediary.emitMessage("<font color=#E60000>Error: " + message + "</font><br>");
break;
}
}
void ConsoleWidget::runCommand(QString commandString, MessageType type){
QStringList elements = commandString.toLower().split(QRegularExpression("\\s+"), QString::SkipEmptyParts);
if(elements.size() <1) return;
addMessage(commandString.toLower(), type);
if(!mCommands.Contains(elements[0].toUtf8().data())){
addMessage("\"" + elements[0] + "\" is not a valid command", Error);
return;
}
mCommands.Contains(elements[0].toUtf8().data())->mFunction(elements, None);
}
void ConsoleWidget::userCommand(){
QString command = mInputWidget->text();
mInputWidget->setText("");
runCommand(command, UserInput);
}
void ConsoleWidget::updateConsoleDisplay(QString message){
mConsoleDisplay->insertHtml(message);
mConsoleDisplay->verticalScrollBar()->setValue(mConsoleDisplay->verticalScrollBar()->maximum());
}
/*
Reimplemented to allow QSizeGrip to set it's parent
*/
void ConsoleWidget::setParent(QWidget *parent){
mResizeGrip->setParent(parent);
QWidget::setParent(parent);
}
/*
Paint event needs to be reimplemented in order to support QStyleSheet
*/
void ConsoleWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void ConsoleWidget::helpCommand(QStringList commandElements, ConsoleWidget::CommandFlag flag){
}