【问题标题】:undefined reference errors in C++C++ 中未定义的引用错误
【发布时间】:2010-05-23 07:08:32
【问题描述】:

我有文件 Record.h 和 Record.cpp。当我只包含 Record.h 文件时,我会收到几个未定义的对这些文件中定义的函数的引用错误。当我还包括 Record.cpp 时,错误就会消失。这是为什么? Record.h 具有它所说的未定义引用的函数的前向声明。

记录.h

#ifndef RECORD_
#define RECORD_

#include <string>
#include <limits>
using namespace std;

#define PKEYSIZE 10
#define STREETNUMSIZE 8
#define STREETNAMESIZE 24
#define RNAMESIZE 30
#define ASTYLESIZE 20
#define YEARSIZE 12

#define MAXBUCKETSIZE 4
#define MAXDIRECTORYSIZE 100

typedef struct
{

        char streetNum[STREETNUMSIZE];
        char streetName[STREETNAMESIZE];
        char rName[RNAMESIZE];
        char aStyle[ASTYLESIZE];
        char year[YEARSIZE];
        char pkey[PKEYSIZE];

} RECORD;

typedef struct
{
    int size;
    int depth;
    RECORD record[MAXBUCKETSIZE];

} BUCKET;


#define HEADERSIZE 2L

#define NODESIZE sizeof(BUCKET)

void addKey(RECORD *rec_ptr, string key);
void addStreetNum(RECORD *rec_ptr, string s);
void addStreetName(RECORD *rec_ptr, string s);
void addRName(RECORD *rec_ptr, string s);
void addAStyle(RECORD *rec_ptr, string s);
void addYear(RECORD *rec_ptr, string s);
void printRecord(RECORD *rec_ptr);
ostream & operator<<(ostream & out, RECORD *r);

string cvt_binary(unsigned int input);
void addRecord(RECORD *r);
void showbucket(int n);

#endif

记录.cpp

int dirDepth = 0;
int numberOfBuckets = 0;
int directory[MAXDIRECTORYSIZE];
BUCKET bucket[MAXDIRECTORYSIZE/MAXBUCKETSIZE];

void addKey(RECORD *rec_ptr, string key)
{
    strncpy(rec_ptr->pkey, key.c_str(), PKEYSIZE);

}

void addStreetNum(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->streetNum, s.c_str(), STREETNUMSIZE);
}

void addStreetName(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->streetName, s.c_str(), STREETNAMESIZE);
}

void addRName(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->rName, s.c_str(), RNAMESIZE);
}

void addAStyle(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->aStyle, s.c_str(), ASTYLESIZE);
}


void addYear(RECORD *rec_ptr, string s)
{
    strncpy(rec_ptr->year, s.c_str(), YEARSIZE);
}

void printRecord(RECORD *rec_ptr)
{

    cout<< "|"
        << rec_ptr->pkey << "|" 
        << rec_ptr->streetNum << "|" 
        << rec_ptr->streetName << "|" 
        << rec_ptr->rName << "|" 
        << rec_ptr->aStyle << "|" 
        << rec_ptr->year << endl;

}


ostream & operator<<(ostream & out, RECORD *r)
{
    out << r->pkey << r->streetNum << r->streetName << r->rName
        << r->aStyle << r->year << endl;
    return out;

}

int bucketread(short rrn, BUCKET *page_ptr)
{
//  long lseek(), addr;
    long addr;

    addr = (long)rrn * (long)NODESIZE + HEADERSIZE;
    lseek(btfd, addr, 0);
    return ( read(btfd, page_ptr, NODESIZE) );
}

int bucketwrite(short rrn, BUCKET *page_ptr)
{
//    long lseek(), addr;
    long addr;
    addr = (long) rrn * (long) NODESIZE + HEADERSIZE;
    lseek(btfd, addr, 0);
    return (write(btfd, page_ptr, NODESIZE));
}

void showbucket(int n)
{
    cout << "loading bucket " << n << endl;
    BUCKET b;
    bucketread(n, &b);
    cout << "there are " << b.size << " records in the bucket" << endl;

}

string cvt_binary(unsigned int input) {
    if(input == 0) return "0"; // trivial case
    string result;
    for(int i = numeric_limits<unsigned int>::digits - 1; i >= 0; --i) {
        if(input & (1 << i)) 
        {
            result += "1";
        } 
        else 
        {
            if(!result.empty()) result += "0";
        }
    }
    return result;
}


string hash (char* key)
{
    int sum = 0;
    int len = strlen(key);
    if (len % 2 == 1) len++; // make len even
    //for an odd length string, use the trailing 0 as part of key
    for (int j = 0; j < len; j +=2)
        sum = (sum + 100 * key[j] + key[j+1]) % 19937;
    return cvt_binary(sum);
}

void copyrecord(RECORD *dest, RECORD *src)
{
    cout << "copying record" << endl;
    addKey(dest, src->pkey);
    addStreetNum(dest, src->streetNum);
    addStreetName(dest, src->streetName);
    addRName(dest, src->rName);
    addAStyle(dest, src->aStyle);
    addYear(dest, src->year);
}

void addToBucket(int n, RECORD *r)
{
    cout << "Adding record " << r->pkey << " to bucket " << n << endl;
    if (bucket[n].size == MAXBUCKETSIZE)
    {
        cout << "Bucket " << n << " is full." << endl;
        // examine bucket depth and directory depth to determine next action
        cout << "Bucket depth: " << bucket[n].depth << endl;
        cout << "Directory depth: " << directory[0] << endl;
    }
    else
    {
        copyrecord(&bucket[n].record[bucket[n].size],r);
        bucket[n].size++;
        bucketwrite(1,&bucket[1]);


    }

}

string getreverse(string key, int num)
{
    if(num==0)
        return "";
    string newstring;
    newstring = key.at(key.length());
    newstring+= getreverse(key.substr(0,key.length()-1),num-1);

    return newstring;
}

void addRecord(RECORD *r)
{
    cout << r->pkey << endl;
    string hashvalue = hash(r->pkey);
    cout << "hash value is " << hashvalue << endl;

    int directoryDepth = directory[0];

    if(directoryDepth == 0)
    {
        directory[1] = 1;
        addToBucket(1, r);
    }
    else
    {
        // use hashing to figure out which bucket to add to
        cout << "The relevant string is" <<  getreverse(hashvalue, directoryDepth) << endl;

    }

}

【问题讨论】:

  • 我使用的是 g++,它通常可以正常工作。
  • 请不要在头文件中使用using namespace std;,只能在实现文件中使用。
  • 即使在实现文件中,也最好只对你经常使用的符号进行,永远不需要带整个命名空间。

标签: c++ include linker header-files


【解决方案1】:

Record.h 具有这些函数的声明。 Record.cpp 具有定义

//Record.h

void foo(); // This is a forward declaration

到目前为止一切顺利。

//main.cpp

#include "Record.h"

int main()
{
    foo();
}

现在 main.o 可以正常编译了。但是,如果您尝试将其链接到一个工作二进制文件中,您会收到一个抱怨 void foo() 未定义。

//Record.cpp

#include "Record.h"

// This is the definition
void foo()
{
    // do various things
}

这可以编译成 Record.o,然后可以与 main.o 链接到一个工作可执行文件中。

【讨论】:

  • void foo(); 是一个声明。 “前进”是没有意义的,没有其他的。编译正常的是main.cpp,而不是main.o。如果你解决这些问题,我会对我的投票感觉更好。
  • @sbi: 由于void foo(); 是对尚未定义的事物的声明(就 main 而言),因此根据定义,它是前向声明——是否还有其他声明事情。实际上是 g++ 正在编译(或者可能是 Phenom),我听说过源文件(main.cpp)和目标文件(main.o)都用作动词的作格形式的主语——我是为了清楚起见,但我自己是一个语法纳粹,我很高兴听到一个合理的论点。
  • @Beta,重新声明是或不是 forward 声明:声明 notforward 的相关实际案例是什么 声明? (为什么要声明已经定义的东西,根据定义,定义也是声明?)至于编译器编译的内容:我的洗碗机清洗脏盘子并产生干净的盘子。同样,我的编译器编译源文件并生成目标文件。
  • @sbi:声明:我恭敬地建议您已经忘记了您要表达的观点。菜肴:不是一个强有力的论据,因为 1)您使用“洗”作为及物动词,这使得它无关紧要,2)我们没有说“洗碗”,以及 3)一个例子没有说明:我们说“面包在烤”,而不是“面团在烤”。
  • @Beta: 1) “Forward”意味着它被声明为before-hand,即定义之前。但是声明还有什么用呢?如果它们出现在定义之后,它们就是重新声明(因为每个定义也是一个声明)。通常,声明用于声明某些东西在定义之前。它们都是前向声明重新声明。 (顺便说一句,我只在课堂上听说过 forward 声明,where it is indeed meaningless, as there are no other forms of class declarations。)
【解决方案2】:

编译器需要通过包含标头来提供声明链接器需要定义。它会在编译器编译源文件时创建的目标文件中找到这些。
我没有使用 gcc,但我认为可以使用所有源文件调用它,然后使用提供的所有目标文件调用链接器:g++ main.cpp record.cpp

【讨论】:

    【解决方案3】:

    我的 C++ 可能有点生疏,但听起来 Record.cpp 没有被编译到解决方案中。如果.cpp 文件不存在,则标头没有可参考 的内容。不要包含 cpp,但请检查您的链接器设置,或者您用于编译程序的命令...它需要包含所有 cpp。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多