2

Trivial as it ought to be, I just cannot figure out how to separate my source code into different files.

My code compiles and executes just fine when it is written as a single source file:

#include <iostream>
using namespace std;

class Greeter{ 
    public:
        void greet();
};

void Greeter::greet(){
    cout << "Hello World!";
}

int main(){
    Greeter greeter;
    greeter.greet();
    return 0;
}

But try as I might, separating the code into separate source files:

Greeter.h

#include <iostream>
using namespace std;

class Greeter{ 
    public:
        Greeter();
        void greet();
};

Greeter.cxx

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

void Greeter::greet(){
    cout << "Hello World!";
}

main.cxx

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

int main(){
    Greeter greeter;
    greeter.greet();
    return 0;
}

always results in a compilation error:

main.cxx:(.text+0x16): undefined reference to `Greeter::Greeter()'
LongTP5
  • 417
  • 3
  • 11
  • 3
    how are you compiling? – kmdreko Jan 24 '19 at 03:51
  • 1
    Simply add the braces in `Greeter.h`, e.g. `Greeter() {};` (avoid `using namespace std;` in any header, instead remove it and just use `std::cout`) Also include header-guards in `Greeter.h` (e.g. `#ifndef _my_class_greeter_h_ #define _my_class_greeter_h_ ... #endif`) – David C. Rankin Jan 24 '19 at 04:07
  • 2
    Look at your code carefully, before and after. You added a `Greeter()` user-defined default constructor when you didn't have one before. – PaulMcKenzie Jan 24 '19 at 04:11
  • See [Default constructors](https://en.cppreference.com/w/cpp/language/default_constructor) paying very careful attention to the text within the `(...)` contained in the first sentence of the description, particularly the `"empty parameter list"` part. – David C. Rankin Jan 24 '19 at 04:14

1 Answers1

4

It is unclear whether the comments solved your problem. In separating your source into a header and multiple sources, your primary problem evidenced by the error is that you include an incomplete constructor for class Greeter in Greeter.h. Specifically, you fail to include "an empty parameter list" to complete the constructor, e.g.

    Greeter() {};   /* default construct */

See cppreference - Default constructors

The next issue you should avoid is including using namespace std; in the header file. See “using namespace” in c++ headers. Instead, simply make your call to cout, std::cout and eliminate the need to include the namespace altogether.

Next, while iostream has proper header guards, you only need to include it in Greeter.cpp (that is the only source making use of an iostream function). You should also include header guards in your Greeter.h to prevent multiple inclusions during compilation. Simply create a #define and check whether or not that is already defined within the header, e.g.

greeter.h

#ifndef my_class_greeter_h
#define my_class_greeter_h  1

class Greeter { 

    public:
        Greeter() {};   /* default construct */
        void greet();
};

#endif

Now every file that includes greeter.h will avoid including it again if my_class_greeter_h is already defined.

greeter.cpp

Your source file with your class function definition is the only source that relies on an iostream call, and is the only file that requires #include <iostream>, e.g.

#include <iostream>

#include "greeter.h"

void Greeter::greet(){
    std::cout << "Hello World!\n";
}

main.cpp

You main.cpp source file need only include your header containing the class definition, e.g.

#include "greeter.h"

int main (void) {

    Greeter greeter;    /* instantiate greeter */
    greeter.greet();    /* call greet() */

    return 0;
}

Both Sources Must Be Compiled

Compiling the separate source files requires that both main.cpp and greeter.cpp be compiled (either compiling greeter.cpp to object or by simply including both .cpp files in your compile string).

Compiling With gcc/clang

$ g++ -Wall -Wextra -pedantic -std=c++11 -Ofast -o main main.cpp greeter.cpp

Compiling With VS (cl.exe)

> cl /nologo /W3 /Ox /EHsc /Femain /TP main.cpp greeter.cpp

(do not accept code until it compiles without warning)

Example Use/Output

In either case, the output is as expected:

$ ./main
Hello World!

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I hesitated to even ask this question as I thought it likely far too trivial and not worth bothering the community. Thankfully, you're answer has not only solved my issue, but shown that even in such a short and basic piece of code, there is a lot of room for "good practice" improvements. Hopefully your answer help others as it did me. Thank you. – LongTP5 Jan 24 '19 at 06:43
  • The very first header to include in `greeter.cpp` should be the corresponding `greeter.h`. This ensures that `greeter.h` is self-contained. The order of additional includes in `greeter.cpp` is less important as long as they are after `greeter.h`. My personal preference is to include project headers next (those in `""`) and system headers last (those in `<>`). – j6t Jan 24 '19 at 07:51
  • Wise point. I generally rely on the standard headers are guarded properly (perhaps wrongly) so they would only be included once regardless of order, but I do like your approach of including your source header as the first ensuring that compilation using is self contained. I can see instances where there would be additional includes (like `iostream here` necessary only for the internal workings of the source file. Thanks. – David C. Rankin Jan 24 '19 at 08:08