2

I wanted to make a friendship between 2 classes by using the method from one class. Even I looked into different tutorials and books and I can't make it works.

Edit:: It works in one file, but I want to make it in separated files - what unfortunately can't do:

Tbase_in_memory.h:

#ifndef FRENDY_TBASE_IN_MEMORY_H
#define FRENDY_TBASE_IN_MEMORY_H

#include <iostream>
#include <string>
#include <fstream>

class base;

class Tbase_in_memory
{
public:
    Tbase_in_memory(int = 2);
    ~Tbase_in_memory();
    void read_to_arrays(base & b);

private:
    std::string *name;
    double      *price_tag;
    int         *code;
    char        *type;
    int         _size;
};

#endif

Tbase_in_memory.cpp:

#include "Tbase_in_memory.h"

using namespace std;

class base;
Tbase_in_memory::Tbase_in_memory(int s)
{
    _size = s;
    name = new string[_size];
    price_tag = new double[_size];
    code = new int[_size];
    type = new char[_size];
}

Tbase_in_memory::~Tbase_in_memory()
{
    delete[] name;
    delete[] price_tag;
    delete[] code;
    delete[] type;
}

void Tbase_in_memory::read_to_arrays(base & b)
{
    string line;
    while (getline(b.file, line)) {
        cout << line;
    }
}

base.h:

#ifndef FRENDY_BASE_H
#define FRENDY_BASE_H

#include <iostream>
#include <string>
#include <fstream>
#include "Tbase_in_memory.h"

class base
{
public:
    base(std::string = "...");
    ~base();
    friend void Tbase_in_memory::read_to_arrays(base & b);
private:
    std::fstream    file;
    std::string     f_name;
};

#endif

base.cpp

#include "base.h"

using namespace std;

base::base(string n)
{
    f_name = n;
    file.open(f_name, ios::in);

    if (!file.good()) {
        cout << "Error";
        cout << string(38, '-');
        exit(0);
    }
}

base::~base()
{
    file.close();
}
#include <iostream>
#include "Tbase_in_memory.h"
#include "base.h"

using namespace std;

int main()
{
    base b("/home/Sempron/Desktop/code");
    Tbase_in_memory a;
    a.read_to_arrays(b);
    return 0;
}

I got errors:

"error: invalid use of incomplete type ‘class base’
     while (getline(b.file, line)) {". 

"forward declaration of ‘class base’
     class base;"
jxh
  • 69,070
  • 8
  • 110
  • 193
Sempron
  • 29
  • 1
  • 4
  • When the compiler tries to parse `friend void Tbase_in_memory::read_to_arrays(base & b);`, there is no `Tbase_in_memory::read_to_arrays`. Digging for a dupe. – user4581301 Sep 06 '19 at 21:13
  • 5
    Move the full declaration of `Tbase_in_memory` above `base`. Forward declare `class base;` above `Tbase_in_memory`. – jxh Sep 06 '19 at 21:19
  • 1
    Unrelated: Rather than a small horde of dynamic arrays you have to allocate and maintain in `Tbase_in_memory`, consider aggregating the data into a single structure and making a single array (`std::vector` if you got 'em) of that structure. Added bonus of the `vector` is you don't have to watch out for the [Rule of Three](https://en.cppreference.com/w/cpp/language/rule_of_three) because `vector` observes the Rule of Five. Right now if you accidentally copy a `Tbase_in_memory`, you're in for a world of hurt. – user4581301 Sep 06 '19 at 21:27
  • By the way no need for dupe. @jxh 's solution is more elegant than any of the dupes I found. – user4581301 Sep 06 '19 at 21:28
  • 1
    Could not reproduce: http://coliru.stacked-crooked.com/a/ab99d7e8575e0f98 – eerorika Sep 06 '19 at 22:01
  • This is the obligatory "why no std::vector' question. – n. m. could be an AI Sep 07 '19 at 13:27

2 Answers2

3

In the file Tbase_in_memory.cpp, you need to also include base.h. Then you can remove the forward declaration in the cpp file.

#include "Tbase_in_memory.h"
#include "base.h"

using namespace std;

Tbase_in_memory::Tbase_in_memory(int s)
{
    //...
jxh
  • 69,070
  • 8
  • 110
  • 193
1

The prototype/definition of the function being given friendship access must exist when the friend reference is declared. Fixed code (with includes):

#include <string>
#include <iostream>
#include <fstream>

using namespace std;

class base;

class Tbase_in_memory
{
public:
    Tbase_in_memory(int = 2);
    ~Tbase_in_memory();
    void read_to_arrays(base & b);

private:
    std::string *name;
    double      *price_tag;
    int         *code;
    char        *type;
    int         _size;
};

class base
{
public:
    base(std::string = "...");
    ~base();
    friend void Tbase_in_memory::read_to_arrays(base & b);
private:
    std::fstream    file;
    std::string     f_name;
};

Tbase_in_memory::Tbase_in_memory(int s)
{
    _size = s;
    name = new string[_size];
    price_tag = new double[_size];
    code = new int[_size];
    type = new char[_size];
}

Tbase_in_memory::~Tbase_in_memory()
{
    delete[] name;
    delete[] price_tag;
    delete[] code;
    delete[] type;
}

void Tbase_in_memory::read_to_arrays(base & b)
{
    string line;
    while (getline(b.file, line)) {
        cout << line;
    }
}

base::base(string n)
{
    f_name = n;
    file.open(f_name, ios::in);

    if (!file.good()) {
        cout << "Error";
        cout << string(38, '-');
        exit(0);
    }
}

base::~base()
{
    file.close();
}

int main()
{
    base b("/home/Sempron/Desktop/code");
    Tbase_in_memory a;
    a.read_to_arrays(b);
    return 0;
}

Ideally, the two classes would be declared in different header files, so you must watch the include order carefully.

memtha
  • 797
  • 5
  • 24
  • Hi! Thank you and @jxh for your answers. It works in one .cpp file, but when I separate classes to different .cpp and .h files - it doesn't work and I really have no idea why. Could you explain how to make it works with headers? I will be really grateful for that. – Sempron Sep 07 '19 at 12:29
  • @Sempron Add `#include "base.h"` to `Tbase_in_memory.cpp` *after all other includes*. That's the `cpp` file, not the `h` file. Optionally remove the class prototype in the same file `class base;` **edit:** @jxh beat me to it. – memtha Sep 08 '19 at 17:15