0
votes

Let me specify exactly what I'm trying to do, I need to split my foo-bar program into five separate files: main, foo.h, foo.cpp, bar.h, bar.cpp. My header files (foo.h and bar.h) are meant to contain the declarations for their corresponding classes, while the c++ files (foo.cpp and bar.cpp) are meant to define the class.

I'm using Visual Studio, and thus far the only file I have showing red flags is my main file. Here is my code thus far, and I will include the errors that are being thrown in my main file:

main.cpp

#include <iostream>
#include "foo.h"
#include "foo.cpp"
#include "bar.h"
#include "bar.cpp"
using namespace std;

int main() {
   Bar b(25); /*I am getting a red flag under the 25, stating there is no constructor that can convert int to Bar*/

   b.func1(); /*I'm getting a red flag under func1 and func2 stating neither of them are members of Bar*/
   b.func2(34);

   return 0;}

foo.h

#ifndef foo_h
#define foo_h
#include "foo.cpp"
 class Foo {};
#endif

foo.cpp

#ifndef foo_c
#define foo_c

#include "foo.h"
#include "bar.cpp"
private:
    int data;
public:
Foo(int d) : data(d) {}

int get_data() { return data; }

virtual void func1() = 0;

virtual int func2(int d) = 0;


#endif

bar.h

#ifndef bar_h
#define bar_h
#include "bar.cpp"
#include "foo.h"
class Bar : public Foo {};
#endif

bar.cpp

#ifndef bar_c
#define bar_c

#include "bar.h"
#include "foo.h"
#include "foo.cpp"

Bar(int d) : Foo(d) {}

void func1() {
    cout << "Inside func1\n";
    cout << "\tData is " << get_data() << endl;
}

int func2(int d) {
    cout << "Inside func2 with " << d << endl;
    cout << "\tData is " << get_data() << endl;
    return d;
}

#endif

My program worked until I split it up, but now it keeps throwing this message at me when I try to compile it, and there are a couple of red flags in my main code. This is what the console tells me:

No suitable constructor exists to convert int to Bar

func1 is not a member of class Bar

func2 is not a member of class Bar

My question is: What am I doing wrong, and is there a better way to go about what I'm trying to do?

Thank you in advance.

2
You do not need to include your cpp files, only the .h files. You though must compile .cpp files and make sure the object files are available at link stage. That is why they are called compilation units.Dmitri Chubarov
Your. cpp files do not need the #ifndef macro blocks because they do not need to be included.Justin Randall

2 Answers

1
votes

There is more than one misconception manifested in this code. Perhaps it is easier to correct them altogether than to describe and discuss them individually.

Let us start from the bottom of the dependency tree. There at the bottom is a virtual class Foo. Here is its correct declaration.

#ifndef foo_h
#define foo_h
class Foo {
private:
    int data;
public:
   Foo(int);
   int get_data();
   virtual void func1() = 0;
   virtual int func2(int) = 0;
 };

#endif

Note that we include the declarations of all its methods in the header file. However the implementation of the nonvirtual methods is moved out into the foo.cpp file.

#include "foo.h"

Foo::Foo(int d) : data(d) { }

int Foo::get_data() { return data; }

Note that we do not need any special devices to protect from multiple inclusion of the .cpp file because we are not going to include it ever.

Now Foo is the parent of the class Bar that does some real work for us. Once again, all its methods are declared within the class declaration.

#ifndef bar_h
#define bar_h
#include "foo.h"
class Bar : public Foo {
public:
   Bar(int);
   void func1();
   int func2(int);
};
#endif

And its implementation is in the corresponding compilation unit called bar.cpp. When implementing a class method we indicate which class the method belongs to by prepending the class name to the method name, e.g. Bar::func1.

#include "bar.h"
#include "foo.h"

#include <iostream>

Bar::Bar(int d) : Foo(d) {};

using namespace std;

void Bar::func1() {
    cout << "Inside func1\n";
    cout << "\tData is " << get_data() << endl;
}

int Bar::func2(int d) {
    cout << "Inside func2 with " << d << endl;
    cout << "\tData is " << get_data() << endl;
    return d;
}

Finally we use it in the main.cpp where only a small change is required.

#include <iostream>
#include "foo.h"
#include "bar.h"
using namespace std;

int main() {
   Bar b(25); 

   b.func1(); 
   b.func2(34);

   return 0;}

Let's now proceed with building our project. If you were using GCC that would've been easy to describe as a sequence of CLI commands. Since you are using Visual Studio, you would have to perform the corresponding actions through the GUI.

  1. First compile Foo

    g++ -c -Wall foo.cpp

  2. Next compile Bar

    g++ -c -Wall bar.cpp

  3. Compile main

    g++ -c -Wall main.cpp

  4. Now link it all together

    g++ -o main foo.o bar.o main.o

  5. Finally run it and voila

    Inside func1
        Data is 25
    Inside func2 with 34
        Data is 25
1
votes

You should never #include .cpp files. Instead, compile each .cpp file into an object file and link them into an executable.

During the preprocessor stage, the compiler takes all of the #included files and treats them as if they were concatenated into one large program. Sometimes, certain files may be #included multiple times. Declarations, in header files, may be repeated, but multiple definitions, from source files, cause errors. (You probably don't have this problem because you used include guards in your source files.)

When creating object files, header files are used by the compiler to check names and types, but the actual definitions are not needed. The definitions that are found are compiled into the object file. The purpose of separate object files is to separate compilation of these definitions into modular units.