C++:链接器错误:未定义的引用仅指向在单独文件中定义的一个特定类成员
C++: Linker error: undefined reference only to one specific class member defined in a separate file
我在尝试编译&链接几个文件,如果你能提供帮助,我会很高兴。
确切的错误信息:
g++-o主列表.cpp主.cpp/tmp/ccv6M2I6.o:在函数
main': main.cpp:(.text+0x219): undefined reference to
列表中::print((const‘
main.cpp:
#include <iostream>
#include "list.hpp"
using namespace std;
void printStats(IntList& l) {
cout << endl << "____________________" << endl;
cout << "Length: " << l.getCount() << endl;
cout << "Min: " << l.min() << endl;
cout << "Max: " << l.max() << endl;
cout << "Average: " << l.average();
cout << endl << "____________________" << endl;
}
int main() {
IntList l = IntList();
for(int i=1; i <= 10; i++) {
l.insert(i, l.getCount() - 1); // works fine
}
printStats(l); // works fine, too
l.print(); // causes the error
return 0;
}
有趣的是:成员函数insert((、min((、max((或average((都不会引起任何问题。这只是打印((。
[EDIT]:不仅仅是print((,还有remove((。
list.hpp:
#ifndef __LIST_HPP__
#define __LIST_HPP__
template <typename T>
class List {
public:
class OutOfBoundsException { };
List();
List(const List& l);
~List();
List& operator=(const List& l);
unsigned int getCount() const;
bool isEmpty() const;
void print() const;
void insert(T value, unsigned int position = 0);
void remove(unsigned int position);
T pop(unsigned int position = 0);
T getElement(unsigned int position) const;
protected:
// double linked list
struct dllist_entry {
T value;
dllist_entry* next;
dllist_entry* prev;
};
dllist_entry* first;
dllist_entry* last;
unsigned int length;
void clear();
dllist_entry* getElementRaw(unsigned int position) const;
};
class IntList : public List<int> {
public:
IntList();
IntList(const IntList& l);
~IntList();
IntList& operator=(const IntList& l);
int max() const;
int min() const;
float average() const;
};
#endif
list.cpp:
#include <iostream>
#include "list.hpp"
using namespace std;
template <typename T>
List<T>::List() {
this->first = NULL;
this->last = NULL;
this->length = 0;
}
template <typename T>
List<T>::List(const List& l) {
this->first = NULL;
this->last = NULL;
this->length = 0;
for(unsigned int i=0; i < l.getCount(); i++) {
insert(l.getElement(i));
}
}
template <typename T>
List<T>& List<T>::operator=(const List<T>& l) {
if(this != &l) {
// Liste leeren
clear();
for(unsigned int i=0; i < l.getCount(); i++) {
insert(l.getElement(i));
}
}
return *this;
}
template <typename T>
List<T>::~List() {
clear();
}
template <typename T>
void List<T>::clear() {
dllist_entry* iter = first;
dllist_entry* next;
while(iter != NULL) {
next = iter->next;
delete iter;
iter = next;
}
length = 0;
}
template <typename T>
unsigned int List<T>::getCount() const {
return this->length;
}
template <typename T>
bool List<T>::isEmpty() const {
return this->length == 0;
}
template <typename T>
void List<T>::print() const {
// aus Performance-Gründen nicht getElement() benutzen
for(dllist_entry* iter = first; iter != NULL; iter = iter->next) {
cout << iter->value << endl;
}
}
template <typename T>
void List<T>::insert(T value, unsigned int position) {
dllist_entry* new_one = new dllist_entry;
new_one->value = value;
if(getCount() > 0) {
if(position < getCount()) {
if(position == 0) {
new_one->prev = NULL;
new_one->next = first;
first->prev = new_one;
first = new_one;
}
// position > 0
else {
dllist_entry* elem = getElementRaw(position);
new_one->next = elem;
new_one->prev = elem->prev;
elem->prev->next = new_one;
elem->prev = new_one;
}
}
else if(position == getCount()) {
new_one->next = NULL;
new_one->prev = last;
last->next = new_one;
last = new_one;
}
else {
throw OutOfBoundsException();
}
}
else {
new_one->next = NULL;
new_one->prev = NULL;
first = new_one;
last = new_one;
}
length++;
}
template <typename T>
T List<T>::pop(unsigned int position) {
T value = getElement(position);
remove(position);
return value;
}
template <typename T>
void List<T>::remove(unsigned int position) {
dllist_entry* elem = getElementRaw(position);
if(getCount() == 1) { // entspricht elem == first && elem == last
first = NULL;
last = NULL;
}
else if(elem == first) {
elem->next->prev = NULL;
first = elem->next;
}
else if(elem == last) {
elem->prev->next = NULL;
last = elem->prev;
}
// Element liegt zwischen Anfang und Ende
// (Wäre das nicht so, hätte getElementRaw() bereits protestiert.)
else {
elem->prev->next = elem->next;
elem->next->prev = elem->prev;
}
delete elem;
length--;
}
template <typename T>
T List<T>::getElement(unsigned int position) const {
return getElementRaw(position)->value;
}
template <typename T>
typename List<T>::dllist_entry* List<T>::getElementRaw(unsigned int position) const {
// schließt den Fall getCount() == 0 mit ein
if(position < getCount()) {
dllist_entry* iter;
// aus Performance-Gründen mit der Suche entweder von vorne oder
// von hinten beginnen
if(position <= (getCount() - 1) / 2) {
iter = first;
for(unsigned int i=0; i < position; i++) {
iter = iter->next;
}
}
else {
iter = last;
for(unsigned int i = getCount() - 1 ; i > position; i--) {
iter = iter->prev;
}
}
return iter;
}
else {
throw OutOfBoundsException();
}
}
IntList::IntList() : List<int>() { }
IntList::IntList(const IntList& l) : List<int>(l) { }
IntList::~IntList() { }
IntList& IntList::operator=(const IntList& l) {
List<int>::operator=(l);
return *this;
}
int IntList::min() const {
// erstes Element separat holen, damit OutOfBoundsException geworfen werden
// kann, wenn Liste leer ist
int min = getElement(0);
for(unsigned int i=1; i < getCount(); i++) {
int value = getElement(i);
if(value < min) {
min = value;
}
}
return min;
}
int IntList::max() const {
// erstes Element separat holen, damit OutOfBoundsException geworfen werden
// kann, wenn Liste leer ist
int max = getElement(0);
for(unsigned int i=1; i < getCount(); i++) {
int value = getElement(i);
if(value > max) {
max = value;
}
}
return max;
}
float IntList::average() const {
if(getCount() > 0) {
int sum = 0;
for(unsigned int i=0; i < getCount(); i++) {
sum += getElement(i);
}
return (float) sum / getCount();
}
else {
return 0;
}
}
很抱歉来源太多,但我担心如果我只发布摘录,可能会意外遗漏一些内容。
记录在案:在我明确声明/定义List.hpp/List.cpp中的析构函数~IntList((之前,我收到了一条类似的错误消息——这次是List::~List((。我实际上希望我甚至不需要声明它,因为在销毁IntList对象时,父类List的析构因子无论如何都会被调用?此外,即使直接在头文件list.hpp中将析构函数定义为"~IntList(({}"也无济于事——直到我将dtor定义移动到list.cpp,错误消息才会消失。
顺便说一句:当我把它放在一个大文件里的时候,整件事汇编得很好。
感谢您花时间查找此错误!:(
模板的定义应该在提供声明的同一文件中。它们无法分离。
因此,要么将list.cpp
的所有定义移动到list.hpp
,要么在list.hpp
中执行此操作
#ifndef __LIST_HPP__
#define __LIST_HPP__
template <typename T>
class List {
//...
};
class IntList : public List<int> {
//...
};
#include "list.cpp" //<----------------- do this
#endif
并从list.cpp
文件中删除行#include list.hpp
。它正在使其循环:
#include <iostream>
//#include "list.hpp" //remove this
using namespace std; //<----- don't do this either - see the note below!
template <typename T>
List<T>::List() {
this->first = NULL;
this->last = NULL;
this->length = 0;
}
//....
作为旁注,请使用using namespace std
。使用完全限定的名称,例如:
std::vector<int> v;
std::sort(...);
//etc
模板化的代码需要在头文件中。
模板是编译器生成的代码,当你在代码中使用它时,它会"按需"生成。
如果它不在头文件中,它就无法找到合适的代码并为特定类型生成代码。
您所做的是创建一个从List<int>
继承的类,然后用自己的实现隐藏成员函数。
但是您没有实现print()
,并且由于文件中没有包含模板的源代码,因此无法生成List<int>.print()
的代码。
编辑:
只是为了澄清为什么只有print()
提出错误:
在main
函数中,您使用了3个函数:getCount()
insert()
和print()
现在,让我们来看一下List<int>
:的实现
在复制构造函数中,您调用List(const List& l)
。。。。
IntList::IntList(const IntList& l) : List<int>(l) { }
该构造函数调用insert()
、getCount()
和getElement()
:
for(unsigned int i=0; i < l.getCount(); i++) {
insert(l.getElement(i));
}
因此,所有这些函数都是在编译类IntList
时创建的IntList
的实现"看到"了模板实现,因此创建了这些函数。
另一方面,print<int>()
只是在main
函数中第一次被调用,它没有"看到"模板实现。
- 用C++中的一个变量定义一个常量
- 在类定义之后定义一个私有方法
- 如何定义一个纯抽象基类
- 为什么我不能在主函数之外定义一个类的对象(它继承了另一个类)?
- 如何定义一个没有重复代码的继承的 const 类成员函数?
- 如何在 c++ 中定义一个将被另一个短语替换的短语?
- 是否可以定义一个以向量<T>作为值的unordered_map?
- 我可以定义一个 constexpr 匿名/未命名变量吗?
- 定义一个 void f(void) 函数,但使用来自同一范围的变量?
- 如何在一个函数中定义一个变量,并在另一个函数中访问和更改它?(C++)
- 为什么不建议使用宏符号常量定义一个固定长度的数组呢
- 在C++中,我想通过使用来自变量(例如字符串)的typename信息,从模板中定义一个类对象
- 为什么我必须显式定义一个由固有类提供的方法
- 定义一个带有缓冲区的函数作为卤化物中的边界框参数
- Visual Studio 2017 C++,如何定义一个"环境变量"'Additional Library Directory'?
- 如何键入定义一个专门的 std::set 模板,使用特定的比较函数实例化
- 仅使用 bool 和 char 定义一个 templete 类
- 定义一个工厂函数,该函数返回指向在此工厂函数中创建的函数的指针
- 是否可以定义一个非模板函数,该函数可以将模板化对象作为参数
- 如何在C 中定义一个副操作员