C++: problem using an external class

Started by
6 comments, last by Zahlman 16 years, 8 months ago
Hi! I'm sorry if this isn't the right forum for my question. I'm learning C++ because I want to make a simple Arkanoid clone. Things were going relatively well when a I started to get an error which I don't know how to avoid. I'm giving you an example using three classes: // a1.h #ifndef A1_H #define A1_H #include <iostream> class a1 { public: a1(); ~a1(); void a_write() { std::cout << "aaaaa\n"; } }; #endif // A1_H // a2.h #ifndef A2_H #define A2_H #include "a1.h" class a2 : public a1 { public: a2(); ~a2(); }; #endif // A2_H // b.h #ifndef B_H #define B_H class a2; class b { public: b(); ~b(); void b_write(a2 *x) { x->a_write(); } }; #endif // B_H // main.cpp using namespace std; #include "a2.h" #include "b.h" int main(int argc, char *argv[]) { a2 a; b _b; _b.b_write(&a); system("PAUSE"); return EXIT_SUCCESS; } I got these errors: …\b.h In member function `void b::b_write(a2*)': 12 …\b.h invalid use of undefined type `struct a2' 4 …\lixo\b.h forward declaration of `struct a2' What am I doing wrong? Because I'm using a pointer to class a2 in class b, shouldn't the declaration "Class a2" be enough for this to work? Thanks for you help! Regards
Advertisement
No.

Move the implementation of bwrite into a CPP file and include the a2 header in there. The dereferencing of the pointer needs the full definition.
Quote:
Because I'm using a pointer to class a2 in class b, shouldn't the declaration "Class a2" be enough for this to work?

Normally yes, however you are attempting to call a function from an undefined
class (hence the compilier does not know what to do):
void b_write(a2 *x) { x->a_write(); }

It seems like you actually have the full definition because of the order in which you're including headers, but this is a *really* messy way of doing things. In general, you want to separate implementation out to .cpp files.

Also, you are declaring constructors and destructors that aren't actually implemented anywhere. This is bad. If you want the constructor and destructor to do nothing (which is reasonable here - there's nothing to initialize or clean up), the correct answer is to *not mention them at all*.(1) The compiler will generate them automatically if you leave them out, but if you make a declaration, you are promising an implementation, and the compiler will write calls to your implementation, and if they don't exist, the linker will complain.

(1) Exception: if your class is a base class that provides 'virtual' functionality, you are going to need a destructor that is also virtual, even if it does nothing. The default implementation isn't virtual, so you will have to declare and implement an empty, virtual destructor. Constructors are still fine, though.

Also, there's no point in writing a 'class' and then making everything 'public'; it does exactly the same thing to just make a 'struct'. (OK, it also affects the public/privateness of inherited things, but here you're inheriting publically anyway.)

Also, you don't need to use namespace std; for anything you're doing, it's valid (and a good idea, for documentation purposes) to accept no arguments in main() if you aren't going to use the argc/argv, returning 0 (EXIT_SUCCESS) happens by default at the end of main() in C++ (EXIT_SUCCESS is a C-ism, and actually you should be required to #include <cstdlib> to get it), and it's a bad idea to pause your programs artificially at the end like that.

Proper organization would look something like (although there are still *style* issues beyond what I mentioned in the last paragraph; but of course that's to be expected when you make a minimal example to demonstrate a problem ;) ) :

// a1.h#ifndef A1_H#define A1_Hclass a1 {public:void a_write();};#endif // A1_H// a1.cpp#include <iostream>#include "a1.h"void a1::a_write() { std::cout << "aaaaa\n"; } // a2.h#ifndef A2_H#define A2_H#include "a1.h"struct a2 : a1{};#endif // A2_H// b.h#ifndef B_H#define B_Hclass a2;struct b{void b_write(a2 *x);};#endif // B_H// b.cpp#include "b.h"#include "a2.h"void b_write(a2 *x) { x->a_write(); }// main.cpp#include "a2.h"#include "b.h"int main(){a2 a;b _b;_b.b_write(&a);}


As for actually solving the problem... check your project configuration. You might have two folders with different versions of b.h, or something. (Just a wild guess, based on the different paths cited in the error message.)
Thanks for your help!


Quote:Original post by Dave
Move the implementation of bwrite into a CPP file and include the a2 header in there.


It worked! But I had to include the a2 header in the b.h, not in the b.cpp. But honestly, I'm afraid of using includes because, sooner or later, I'll get errors of multiple declaration, situations where I include a class header file in some other class .h file and I get errors because the class where I include the header doesn't know the class... Well newbie talk, isn't it?! :) Once again, thanks!
Quote:Original post by neige
What am I doing wrong? Because I'm using a pointer to class a2 in class b, shouldn't the declaration "Class a2" be enough for this to work?


The rules are a little bit more complex.

  • A pointer to an undefined class may only be passed by copy or reference, compared with another pointer of the same type, converted to a void* pointer, or dereferenced (this yields a reference to an undefined class). It may not, in particular, be offset. Neither can it be cast to a base class pointer, but this is a non-issue since knowledge of inheritance requires the class to be defined.

  • A reference to an undefined class may only be used to initialize other references (including reference arguments in functions) or with the address-of operator. You may not access its members or apply the sizeof operator to it. You may not pass it by value to a function, but this is also a non-issue since the function could not be defined in the first place.


Zahlman: I have the constructor and destructor in the cpp files, but I just posted the headers here. Well, the "EXIT_SUCCESS" is something that my Dev-C++ created automatically! But thank you for all of your advices! :)
Quote:Original post by neige
Thanks for your help!


Quote:Original post by Dave
Move the implementation of bwrite into a CPP file and include the a2 header in there.


It worked! But I had to include the a2 header in the b.h, not in the b.cpp.


Not if you

Quote:
Move the implementation of bwrite into a CPP file


, you won't. Or shouldn't, anyway.

Quote:But honestly, I'm afraid of using includes because, sooner or later, I'll get errors of multiple declaration, situations where I include a class header file in some other class .h file


That's why you use include guards and organize things properly.

(Feel free to include headers from other headers, where it makes your life easier. But first see if a simple forward declaration will do the trick.)

As a rule, headers should be "idempotent": meaning, if you think you need to #include A, you should never have to #include B as well in order to get what you think including A is supposed to do for you. :) (The easiest way to do that is, often, to #include B *from* A.)

This topic is closed to new replies.

Advertisement