Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

prickly

macrumors newbie
Original poster
Oct 13, 2008
3
0
In my experience the appropriate way to include external class files in C++ is like so:

Code:
#include "ClassTest.h"

-Where the interface is declared in the header file (ClassTest.h) and the implementation is defined in the source file (ClassTest.cpp). For some reason though, when I try to compile my main source file (main.cpp) like so:

$ g++ main.cpp

I get the following error:

Undefined symbols:
"ClassTest::test()", referenced from:
_main in cciBbM2p.o
ld: symbol(s) not found
collect2: ld returned 1 exit status


Strangely, if I include my external source like so:

Code:
#include "ClassTest.cpp"

everything works fine! Obviously this isn't the end of the world because it does in fact work, but I am confused about why I can't include the .h file in the standard fashion. Am I missing some compiler flags or just doing something silly? Here is my complete source just in case:

----------
main.cpp
----------
Code:
#include <stdio.h>
#include "ClassTest.h"

ClassTest tester;

int main(void)
{
  tester.test();
  return 0;
}
-------------
ClassTest.h
-------------
Code:
class ClassTest
{
  public:
    void test(void);
};

---------------
ClassTest.cpp
---------------
Code:
#include "ClassTest.h"

void ClassTest::test(void)
{
  printf("hello! \n");
}
 

Sander

macrumors 6502a
Apr 24, 2008
521
67
It's not the compiler which is complaining, it's the linker. You are including the header fine just fine, so the compiler knows what your ClassTest looks like. However, the linker needs to put the actual code for it into the executable. Try specifying both main.cpp and classtest.cpp on your command line.
 

prickly

macrumors newbie
Original poster
Oct 13, 2008
3
0
Ok, after reading a bunch based on what you said, I can see that ld is the linker and is the program throwing the error message - but I still can't figure out what exactly it is I am supposed to do to fix this...

I tried:

$ g++ main.cpp classtest.cpp


and it does work, but I thought part of the whole point of using #includes was so that you wouldn't have to explicitly compile every dependency? Is there anything really wrong about #including the ClassTest.cpp file?
 

autorelease

macrumors regular
Oct 13, 2008
144
0
Achewood, CA
Ok, after reading a bunch based on what you said, I can see that ld is the linker and is the program throwing the error message - but I still can't figure out what exactly it is I am supposed to do to fix this...

I tried:

$ g++ main.cpp classtest.cpp


and it does work, but I thought part of the whole point of using #includes was so that you wouldn't have to explicitly compile every dependency? Is there anything really wrong about #including the ClassTest.cpp file?

Including .cpp files is very bad practice! :p

What would happen if you decided to add a second .cpp file that needed to use your ClassTest class? If you include that .cpp file in two different places, the methods in ClassTest.cpp will get defined twice, confusing the compiler and throwing all kinds of errors about duplicate symbols.

You should only #include files that contain statements that don't define anything, like class declarations and function prototypes; i.e. .h files. I like to say that ".h files shouldn't contain any actual code." The compiler can find a bunch of identical prototypes for the same function and not care (which is why header files work); but if it finds more than one function definition, it will fail.

In summary, #include isn't supposed to tell the compiler which files you want to build. It's used for communicating information about your classes and functions to your multiple source files.

If you want to simplify the compilation of multiple source files, look into creating a simple Makefile or using an IDE like Xcode. Knowing how to use make is a very handy skill.
 

ChrisA

macrumors G5
Jan 5, 2006
12,919
2,173
Redondo Beach, California
Ok, after reading a bunch based on what you said, I can see that ld is the linker and is the program throwing the error message - but I still can't figure out what exactly it is I am supposed to do to fix this...

I tried:

$ g++ main.cpp classtest.cpp


and it does work, but I thought part of the whole point of using #includes was so that you wouldn't have to explicitly compile every dependency? Is there anything really wrong about #including the ClassTest.cpp file?

You are right, "g++ main.cpp classtest.cpp" is an expedient. The "correct way is

g++ -c classtest.cpp -o classtest.o
g++ main.cpp classtest.o -o main

But the even more "correct" way is just

make main

But for that to work you will need to create a "Makefile" that describes the dependencies so that make will know how to build only the files that change.
OK Makefiles are hard to write so we have GNU automake, autoconf and libtool that can write good portible Makesfiles for you OK those those GNU tools are hard to use to we have xcode.

All of the above will work. Which to use depends on the size of your project. I'm working on one now that uses over 150 source files so I'm using the GNU auto tools but for a simple project "g++ main.cpp classtest.cpp" is reasonable. But if you want to learn how it is done write a Makefile (see man make)
 

Sander

macrumors 6502a
Apr 24, 2008
521
67
Ok, after reading a bunch based on what you said, I can see that ld is the linker and is the program throwing the error message - but I still can't figure out what exactly it is I am supposed to do to fix this...

I tried:

$ g++ main.cpp classtest.cpp


and it does work, but I thought part of the whole point of using #includes was so that you wouldn't have to explicitly compile every dependency? Is there anything really wrong about #including the ClassTest.cpp file?

Correct. But if you use

$ g++ myprogram.cpp


then this is actually a "convenience shortcut" to do compilation and linking in one step. If your program gets bigger and needs to be split up in multiple source files, you can compile each of these independently:


$ g++ -c source.cpp


which will give you an "object file" (ending in .o). The "-c" stands for "compile only". Then, when you have this set of .o files, you can link them together into your final executable:


$ g++ -o myprogram source1.o source2.o source3.o


in which case you are invoking the linker (the "g++" command is sort of a shortcut in itself, too) to link all the .o files into a program called "myprogram" (the "-o" stands for "name the output file as follows").

When you later make changes to a single source file, you only need to recompile this single source file and can link the resulting new object file with the other, older ones.

This process can (and should) be automated because of a thing called "dependencies". Writing a simple Makefile for a project consisting of a handful of source files is not too difficult. Using Xcode projects is an (even easier) alternative. Note, though, that until your project reaches a certain "critical mass" you might as well recompile everything every time, if you want to focus on programming instead of learning about build tools.

Good luck,
Sander
 

prickly

macrumors newbie
Original poster
Oct 13, 2008
3
0
Wow, thanks a lot for all of your help everyone. I've learned a ton about c/c++ conventions, how to use the GCC compiler from terminal and actually read good portions of the GCC make manual which has admittedly blown my mind a bit in all of its complexity. But something really cool that I found just from reading the GCC man pages is the preprocessor option -M that searches your source file and automatically generates the appropriate dependency rules for make so you don't actually have to write them explicitly yourself. Funny that it was just one flag, but seeing as I didn't even know what make WAS at the beginning I never would have found it...
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.