1

I'm trying to create a function below in my CreateReport class called load() that copies all the records (data) from my graduate.dat file into my static vector of Record pointers called primaryCollection. I created a Record class with variables that make up each Record, and in my load() function in createReport.cc I attempted to read each line in the file, create a Record object with each line, add it to my vector, and then print everything in primaryCollection.

The problem is every time I attempt to use primaryCollection, I keep getting the error:

CreateReport.o: In function `CreateReport::CreateReport()':
CreateReport.cc:(.text+0x43): undefined reference to `CreateReport::primaryCollection'
CreateReport.o: In function `CreateReport::load()':
CreateReport.cc:(.text+0x2ac): undefined reference to `CreateReport::primaryCollection'
CreateReport.cc:(.text+0x31d): undefined reference to `CreateReport::primaryCollection'
CreateReport.cc:(.text+0x32f): undefined reference to `CreateReport::primaryCollection'

I get 4 undefined references for the 4 times I mention primaryCollection in createReport.cc. I'm not sure if I'm initializing primaryCollection correctly and if that is whats causing these undefined references. I don't know if this is relevant to my problem, but CreateReport is also an abstract class and has a few subclasses called ReportOne, ReportTwo, etc.

primaryCollection is supposed to be a static vector of Record pointers and I'm also not allowed to use std::map for this task.

I would appreciate any help with this issue. I looked at this post Undefined reference to static variable c++ but I still don't really understand what to do. I'm not allowed to make global variables and I'm dealing with a collection rather than a single variable.

My graduate.dat file is formatted like below in the format < year province degree >

2000 AB Bachelor's 
2005 AB Bachelor's 
2005 MB College 

Each line basically represents a Record. So the first record here is 2000 AB Bachelor's

EDIT: So I made changes to my code based on the comments by adding the line vector<Record*> CreateReport::primaryCollection; above my constructor, but it gives me the error:

CreateReport.cc:13:34: error: conflicting declaration ‘std::vector<Record*> CreateReport::primaryCollection’
 vector<Record*> CreateReport::primaryCollection;
                                  ^~~~~~~~~~~~~~~~~
In file included from CreateReport.cc:5:0:
CreateReport.h:23:33: note: previous declaration as ‘std::vector<Record*>* CreateReport::primaryCollection’
    static std::vector<Record*>* primaryCollection; //STL vector of record pointers
                                 ^~~~~~~~~~~~~~~~~
CreateReport.cc:13:34: error: declaration of ‘std::vector<Record*>* CreateReport::primaryCollection’ outside of class is not definition [-fpermissive]
 vector<Record*> CreateReport::primaryCollection;

Any ideas how to fix this?

Record.h

#ifndef RECORD_H
#define RECORD_H

    #include <iostream>
    #include <string>

    class Record{
      public:
            Record(int = 0, string = "", string = "");
            ~Record();
        
        private:
            int year;
            string province;
            string degree;
    };
    #endif

Record.cc

#include <iostream>
#include <string>
using namespace std;
#include "Record.h"

Record::Record(int i1, string s1, string s2) : year(i1), province(s1), degree(s2){}

Record::~Record(){}

CreateReport.h

#ifndef CREATEREPORT_H
#define CREATEREPORT_H

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <cstdlib>

#include "Record.h"

class CreateReport{
  public:
    CreateReport();
    static void load();
  
  protected:
    static vector<Record*> primaryCollection; //STL vector of record pointers
  
};
#endif

CreateReport.cc

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

#include "CreateReport.h"

vector<Record*> CreateReport::primaryCollection;

CreateReport::CreateReport(){ 
}

void CreateReport::load(){
    int year;
    string province, degree;

    ostream_iterator<Record*> outItr(cout);

    ifstream infile("graduate.dat", ios::in); 

    if (!infile) {
        cout << "Error: could not open file" << endl;
        exit(1);
    }

    while (infile >> year >> province >> degree) { //as long as were not at end of file
        Record* record = new Record(year, province, degree); //create Record object with this data
        primaryCollection->push_back(record); //undefined reference
    }
  
    cout<<endl<<"List of Records:"<<endl;
    copy(primaryCollection->begin(), primaryCollection->end(), outItr); //2 undefined references
}
  • You need to provide a definition for `primaryCollection` as well as a declaration. Something like `vector CreateReport::primaryCollection;` as a line in CreateReport.cc. Note that `primaryCollection = new vector();` will fail to compile anyway because `primaryCollection` isn't a pointer. I also assume you don't actually want to leak memory every time you initialize a `CreateReport` object when there's only supposed to be a single static instance of `primaryCollection`. – Nathan Pierson Dec 09 '20 at 04:51
  • I'd also point out that you really don't need to use any pointers for what you've shown. There's no reason for `primaryCollection` to be a `vector` instead of a `vector`, and you don't need to treat `primaryCollection` as a pointer to a `vector` instead of a `vector`. – Nathan Pierson Dec 09 '20 at 04:53
  • Rather than deleting your [previous question](https://stackoverflow.com/questions/65209471/c-initializing-static-vector-of-pointers-causes-undefined-reference) and reposting it, you could have asked for clarification if you didn't understand how the duplicate applies to your question. In this case, you've _declared_ `primaryCollection` but haven't _defined_ it, which you need to do once somewhere in a translation unit (a .cpp file) by including `vector CreateReport::primaryCollection;`. – 1201ProgramAlarm Dec 09 '20 at 05:00
  • Hello, thank you for your help! I added the line vector CreateReport::primaryCollection; to replace what I had in my CreateReport constructor, but it gives me the error: qualified-id in declaration before ‘;’ token with an arrow pointing to the ; at the end of the line that was added. – lizakoshy101 Dec 09 '20 at 05:12
  • You don't put it in the constructor, you put it as a standalone line in the source file. – Nathan Pierson Dec 09 '20 at 05:17
  • I placed it outside of the functions in CreateReport.cc, above the constructor and got the errors "conflicting declaration ‘std::vector CreateReport::primaryCollection’" and "error: declaration of ‘std::vector* CreateReport::primaryCollection’ outside of class is not definition [-fpermissive]" – lizakoshy101 Dec 09 '20 at 05:25

2 Answers2

0

Three major problems:

  1. Using std::vector<*Record> cause many un-necessary difficulties;
  2. For static member vector, a extra definition outside the class is necessary.std::vector<Record> CreateReport::primaryCollection;. This erases the undefined error message.
  3. Using copy to std::cout doesn't work, it provide no method of printing Record. Suggest to write a output overload.

Based on these, I provide a version as follows (mixed all headers together.)

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;

class Record{
  public:
        Record(int = 0, string = "", string = "");
        ~Record()=default;
 friend  std::ostream& operator<<(std::ostream&, const Record&);
    private:
        int year;
        string province;
        string degree;
};
// **** output overload for Record ***********
    std::ostream& operator<<(std::ostream& os, const Record& rd)
    {
        os << "year = " << rd.year << "  prov = " << rd.province << "  degree = " << rd.degree << std::endl;
       return os;
    }
// ****** end of output overload **************
Record::Record(int i1, string s1, string s2) : year(i1), province(s1), degree(s2){}
//end of Record.cc
//
class CreateReport{
  public:
    CreateReport() = default;
   void load();

  protected:
    static vector<Record> primaryCollection; 
};
//***************** you need this line ********************
vector<Record> CreateReport::primaryCollection;
//*********************************************************
void CreateReport::load(){
   int year;
   string province, degree;

   ifstream infile("graduate.dat", ios::in);

   if (!infile) {
      cout << "Error: could not open file" << endl;
      exit(1);
     }

    while (infile >> year >> province >> degree) {
        primaryCollection.push_back( Record(year, province, degree) );
    }

    cout<<endl<<"List of Records:"<<endl;
    for (int i = 0; i<primaryCollection.size(); ++i ) std::cout << primaryCollection[i];
}
int main()
{
    CreateReport mime;
    mime.load();
}
ytlu
  • 412
  • 4
  • 9
0

Second version using `Record*` for `std::vector primaryCollection`.

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;

    class Record{
      public:
          Record(int = 0, string = "", string = "");
          ~Record()=default;
    friend  std::ostream& operator<<(std::ostream&, const Record&);
        private:
           int year;
           string province;
           string degree;
    };
// **** output overload for Record ***********
    std::ostream& operator<<(std::ostream& os, const Record& rd)
    {
        os << "year = " << rd.year << "  prov = " << rd.province << "  degree = " << rd.degree << std::endl;
        return os;
    }
// ****** end of output overload **************
Record::Record(int i1, string s1, string s2) : year(i1), province(s1), degree(s2){}
//end of Record.cc
//
class CreateReport{
  public:
    CreateReport() = default;
    void load();

  protected:
    static vector<Record*> primaryCollection; //STL vector of record pointers
};
//***************** you need this line ********************
std::vector<Record*> CreateReport::primaryCollection;
//*********************************************************
void CreateReport::load(){
    int year;
    string province, degree;

    ifstream infile("graduate.dat", ios::in);

    if (!infile) {
        cout << "Error: could not open file" << endl;
        exit(1);
    }

    while (infile >> year >> province >> degree) {
            Record *a = new Record(year, province, degree);
        primaryCollection.push_back( a );
    }

    cout<<endl<<"List of Records:"<<endl;
    for (int i = 0; i<primaryCollection.size(); ++i ) std::cout << *primaryCollection[i];
}
int main()
{
    CreateReport mime;
    mime.load();
}
ytlu
  • 412
  • 4
  • 9