Sign in to follow this  
Adam1942

Custum Classes in Custom Headers

Recommended Posts

Adam1942    122
Hello everyone. This isn't a game programming issue, but its a programming issue none the less. A friend of mine asked me to create a program for him to determine a starting chemical based on a series of options that the user can select. Unfortunatley there are different options for the different types of chemcials (alcohols, ketones, etc). I figured the best approach would be to create a different class for each type of chemical. I quickly realized that a large amount of code would be required just to create these classes so I thought that maybe I should split them into different headers which I would then include in the main program. However I am having some trouble with headers. I started out with a base class with contains the 2 properties that all the different chemicals will have in common, but I can not get my header file to compile. Here is my code: chemical.h #ifndef GUARD_chemical_h #define GUARD_chemical_h #include<iostream> #include<string> using namespace std; class Chemical { public: ostream& operator <<(ostream& out); private: string startingMaterial; }; #endif chemical.cpp #include "chemical.h" using namespace std; ostream& operator <<(ostream& out) { out << startingMaterial; return out; } I'm sorry if this code is atrocious, i've never made custom headers before :(

Share this post


Link to post
Share on other sites
Captain P    1092
Both look fine to me, except that in chemical.cpp, you need to specify that the function you're implementing is a member of the Chemical class:
ostream& Chemical::operator <<(ostream& out) { ... }


I'm not so convinced that you need a separate class for every different chemical type. Is there no way to parametrize the differences - that is, having a single, or a few, different classes, where the different chemical types are modeled by multiple instances that contain different values for certain aspects?

EDIT: Oh, and you'll want to remove that 'using namespace std' statement from your header file. It'll cause every other file that includes that header to also use the std namespace - this is sometimes called namespace pollution, as it goes against the purpose of namespaces (that is, to avoid name clashes).

Share this post


Link to post
Share on other sites
Adam1942    122
So I'm guessing if I remove using namespace std from my .h file then i'll have to preface everything from that namespace with std:: right?

Also the header file still won't compile, It may be my compiler, I press compile and nothing happens. Normally I'd get a compilation complete message or a list of errors, but for the .h file i get nothing. I'm using Dev-C++ in case this happens to be a well documented issue.

As to wither or not a class for each chemical is the right route I can't really say. All I know is that my friend said they all (or most all) have different options, so the only option I saw was either create one super chemical class which had every single property or write a series of simpler classes. I chose multiple classes since it felt more organic to say "this is a ketone, it has property X and property Y" than to say "this instance of super chemical has property X and property Y, therefore its a ketone". I guess its just a matter of preferance.

Share this post


Link to post
Share on other sites
Cornstalks    7030
Quote:
Original post by Adam1942
So I'm guessing if I remove using namespace std from my .h file then i'll have to preface everything from that namespace with std:: right?

Yes, and that's a good thing.

Quote:
Original post by Adam1942
Also the header file still won't compile, It may be my compiler, I press compile and nothing happens. Normally I'd get a compilation complete message or a list of errors, but for the .h file i get nothing. I'm using Dev-C++ in case this happens to be a well documented issue.

Header files are not compiled. Only source (.cpp) files are. Though when you #include "header.h", you are literally copying all of the contents in "header.h" into the current file (like your source file, which does get compiled).

Quote:
Original post by Adam1942
As to wither or not a class for each chemical is the right route I can't really say. All I know is that my friend said they all (or most all) have different options, so the only option I saw was either create one super chemical class which had every single property or write a series of simpler classes. I chose multiple classes since it felt more organic to say "this is a ketone, it has property X and property Y" than to say "this instance of super chemical has property X and property Y, therefore its a ketone". I guess its just a matter of preferance.

It's not so much preference as it is what properties they hold and how those properties are used. You could probably group similar properties that all chemicals have (like molar mass, acidity, things like that) into the class. You could also create a list of properties (like a std::vector or std::map or something). I guess in the end it depends on what you want to do with those properties and how you're using them. Creating a class for every chemical seems like overkill though. There is definitely a way to simplify this.

Share this post


Link to post
Share on other sites
Adam1942    122
Well I may revise the implimination design once my friend gets me a list of all the chemical types and the properties he needs.

my .h file still has a couple errors in it, but I think they are all tied into this one:

declaration of 'operator<<' as non-function.

Here's the code again

#ifndef GUARD_chemical_h
#define GUARD_chemical_h

#include<iostream>
#include<string>

class Chemical
{
public:
ostream& operator<<(ostream& out); //error is in this line

private:
std::string startingMaterial;
};

#endif

Share this post


Link to post
Share on other sites
Adam1942    122
Oops! It still isn't working though, I've realized another mistake in my cpp file, but that also didn't get rid of the error. I do know it is a namespace issue since if I switch the postions of using namespace std and #include "chemical.h" in the cpp file the error disappears. Here is the code again for referance:

chemical.h

#ifndef GUARD_chemical_h
#define GUARD_chemical_h

#include<iostream>
#include<string>

class Chemical
{
public:

std::ostream& operator<<(ostream& out);

private:
std::string startingMaterial;
};

#endif

chemical.cpp

#include "chemical.h"
using namespace std;


ostream& Chemical::operator <<(ostream& out)
{
out << startingMaterial;
return out;
}

btw should I be using std:: in the cpp file as well?












Share this post


Link to post
Share on other sites
Captain P    1092
Check your header carefully. You've still forgotten the namespace identifier somewhere.

'Using' a namespace in a .cpp file is ok, if you're using items from that namespace a lot... namespaces are mostly there to prevent name clashes, so if there's a risk for that, you'd better just prefix everything with it's namespace. In this case, you've stated that you're using the std namespace, so everything from that namespace no longer needs the std:: prefix in that .cpp file.

Share this post


Link to post
Share on other sites
ravengangrel    406
Quote:
Original post by Captain P
'Using' a namespace in a .cpp file is ok, if you're using items from that namespace a lot... namespaces are mostly there to prevent name clashes, so if there's a risk for that, you'd better just prefix everything with it's namespace. In this case, you've stated that you're using the std namespace, so everything from that namespace no longer needs the std:: prefix in that .cpp file.


Also, if you only use the items from std in a couple of functions or so, you can write the "using namespace std" inside the function definition.

Share this post


Link to post
Share on other sites
Cornstalks    7030
You're doing it wrong. There, now I've got your attention. Overloading the << operator in a class/struct works like this

type_one operator << (type_two right_hand);

So in your code, out would be on the right hand of the << operator. Meaning your code would look like this:

Chemical chemical;
chemical << std::cout;

Obviously, this is not what you want. You have to declare a function that is a friend of Chemical, like this:
class Chemical
{
public:
friend std::ostream& operator << (std::ostream& left_hand, const Chemical& right_hand);
};

// Later, you define the function like this:
std::ostream& operator << (std::ostream& left_hand, const Chemical& right_hand)
{
left_hand << right_hand.startingMaterial;
return left_hand;
}

// And this is how you invoke the function:
Chemical chemical;
std::cout << chemical;

Of course, you'll probably want to pick better names for your parameters. I just wanted to make it clear where each parameter was in respect to the operator.

And make sure you're using std:: everywhere that deals with something from the std namespace (unless you've put a using directive, which we've talked about). Notice in your header the parameter doesn't have std:: prefixed to it. Try that and let us know how it goes.

Share this post


Link to post
Share on other sites
Adam1942    122
yeah I got the std stuff to work out fine. I'd seen that friend method before, but my plan was to use this class as an abstract base class which would be the parent of many further classes. If I remember correctly friend functions are not part of the class and would not be subject to inheritance. I was hoping that the language would determine that when I sent an Chemical object(or a derivative of that class) to cout I wanted it to call the overloaded member function and use that object as the 2nd parameter implicitly. Thinking about it that assumes a lot, it probley can't be done that way.

In the end I realized an inherent flaw in my design scheme for the program, which can only be solved with an additional layer of abstraction. Since it's only a matter of a single variable and overloading one operator I decided to trash the abstract class and just retype the code each time. However I am extremely grateful for everyone's help, I learned a lot about what exactly #include means.

Share this post


Link to post
Share on other sites
Zahlman    1682
Like Mike said. Notice that he defines the parameter for operator<< as const Chemical&. This is a promise that outputting a Chemical will not actually change the Chemical. This is a good promise to make and a good promise to keep, too.

By the way, you can use "using namespace std;" in the .cpp file (although some people think it's poor style), but putting it in the .h file is an especially bad idea. Why? Because .h files are literally copied and pasted in by the preprocessor, so any .cpp file that #includes the .h file is stuck with that 'using' declaration at the top, and has no way to "un-use" namespace std. That's bad because it can unexpectedly change the meaning of code in the .cpp file.

Also, notice how you're supposed to return an ostream& from the operator<< overload, specifically the same one that was passed in? That's so that chaining will work. Guess what - the built-in overloads do the same thing, so when you use the built-in overload for the string, you can return the result directly. ;) (By the way, the ostream& parameter is not const because the stream object does change when it outputs something - in particular, it might update an internal buffer of text. But it's still the same ostream before and after, just in a different state - that's what the passing and returning by reference does. If we passed or returned by value, it would make a copy. However, std::ostream instances are not copyable, so that fails to compile.)

A recap:

The .h file should look something like this:

#ifndef CHEMICAL_H
#define CHEMICAL_H

#include <iostream>
#include <string>

class Chemical {
std::string startingMaterial;

public:
friend ostream& operator<<(ostream& out, const Chemical& c);
};
#endif



And the .cpp file might then look like:

#include "chemical.h"

// Just to show that you can do it here. Although it would be easier in
// this case to just write std::ostream twice. :)
using namespace std;

ostream& operator <<(ostream& out, const Chemical& c) {
return out << c.startingMaterial;
}

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by Adam1942
but my plan was to use this class as an abstract base class which would be the parent of many further classes. If I remember correctly friend functions are not part of the class and would not be subject to inheritance. I was hoping that the language would determine that when I sent an Chemical object(or a derivative of that class) to cout I wanted it to call the overloaded member function and use that object as the 2nd parameter implicitly. Thinking about it that assumes a lot, it probley can't be done that way.


Function calls in C++ are only polymorphic on the first parameter (or in the case of member functions, on the called-upon object): the code path will depend on exactly what kind of ostream you use, but not upon what kind of Chemical you use.

However, you can deal with that by having the operator overload call a virtual function of the Chemical.

That looks like:


class Chemical {
std::string instanceData;
public:
virtual void printTo(ostream& out);
// The operator overload doesn't need to be a 'friend' any more, because it
// will be implemented in terms of printTo(), which is 'public'.

// As a rule of thumb, a base class with any abstract member functions should
// also have a virtual destructor.
virtual ~Chemical();
};

Chemical::~Chemical() {}

void Chemical::printTo(ostream& out) {
out << instanceData;
}

ostream& operator<<(ostream& out, const Chemical& c) {
c.printTo(out);
return out;
}

// Now you can go ahead and define derived classes, and have them override
// the base printTo().



Quote:
In the end I realized an inherent flaw in my design scheme for the program, which can only be solved with an additional layer of abstraction.


Tell us about it. You might not have gone down the wrong path like you thought. Or there might be yet more options.

Share this post


Link to post
Share on other sites
Adam1942    122
Well my initial plan was to write up a header that contained the abstract base class and then use this class to be a parent class for multiple other classes. I was also intending to put these classes in their own header files, since there are going to be upwards of 20 and I thought that all of them in one cpp file would be too clutered. Unfortunatly I realized that when I imported all those 20 sub classes I'd end up copying the code for the base class into the main program upwards of 20 times. The only way I could see to fix this would be to write yet another header, contraining all the headers (parent and child classes)and use just that one in the main program (so that the base class' code is only copied once). But this seems like overkill for just two properties.

And the reason I didn't use a virtual function is just because I wanted some practice overloading operators, now that I know that I can't do it the way I had intended I'll just stick to virtual functions unless I need to overload.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this