Jump to content
  • Advertisement
Sign in to follow this  
algumacoisaqualquer

Code design in C++

This topic is 4037 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

First off, I have been programming in C++ for some time, but not really in the right way... so what I'm trying to do right now is to learn OO programing, as oposed to "C with classes". Basically I have a c_bitmap class that is a fancy array of the s_color struct. I can build it from another bitmap or from the ground up, and I can paint other bitmaps on top of it. But now I wanted to have a way to build my bitmap based on a filename. My initial idea was to have a overloaded constructor that simply take the filename and a bool by reference (bool& success), but I was wondering if that's a nice solution. Particulary, I was hoping to some day reuse this code in a PalmOS program, and I'm not shure if file reading works differently in that plataform. However, doesen't that breakes encapsulation? I mean, I keep thinking that my bitmap class shouldn't care about loading files, but if that class doesen't care, then who will? Anyway, my other idea was to have a function returning a c_bitmap with the file already loaded, but this doesen't make much sense to me, as that function would need access to pretty much everything inside the c_bitmap class. Is there something I'm missing here? Or I'm just overreacting about my initial problem? Thank you!

Share this post


Link to post
Share on other sites
Advertisement
In my oppinion haveing a bitmap class know how to load a bitmap makes perfectly good sense. It's a bitmap class it should know everything about bitmaps.

Quote:
Original post by algumacoisaqualquer
However, doesen't that breakes encapsulation? I mean, I keep thinking that my bitmap class shouldn't care about loading files, but if that class doesen't care, then who will?


one way to over come this is to make it so the class loads the bitmap from memory. So you would have a seperate resource manager that can take a file and load it into memory as raw data. then you pass that raw data to your bitmap class and it translates it to its form of bitmap image.

Share this post


Link to post
Share on other sites
I guess the OO way would be more like this:
// java like language
IStream stream = new FileStream("filename"); // MemoryStream, NetStream etc
ILoader loader = new ImageBmpLoader(stream); // or LoaderFactory.buildLoader(stream);
Image img = new loader.load(); // image can be a vector, or bitmap based, which in turn can be range based or float based and indexed or not or some other storage type.


and get rid of the "bool& success", throw an exception instead.

Share this post


Link to post
Share on other sites
Separate. The in-memory representation of an image should be independent from the on-disk representation of the file from which it was extracted. Ultimately, you want to have something like:


In-Memory Memory-Map

32-bit Bitmap -\ /- BMP format -\
| |- TGA format -| /- disk
24-bit Bitmap -| |- PNG format -| |
+--- loader ---+- JPEG format -+--- reader ---+- memory
8-bit Bitmap -| |- GIF format -| ^ |
| |- TIFF format -| | |- network
RLE Bitmap -/ \- SVG format -/ \--zip-/


I would consider:

img::bitmap
read_bitmap(const std::string & name, //< Read from this file
const img::format & fmt) //< Expect this image format
{
// Open the file in binary mode
std::fstream file(name, std::ios::binary);

// Create a loader for that file and format
img::loader input(file, fmt);

// Create a bitmap without initializing its contents, and set
// its internal representation mode to '24-bit'
img::bitmap out(img::no_init, img::bitmap::bpp24);

// Use the generic communication between "loader" and "bitmap",
// independent of their internal representation.
input >> out;

// Return the bitmap. This operation should be made cheap by
// using a COW implementation.
return out;
}

int main()
{
// Get a bitmap
img::bitmap image =
read_bitmap("foo.png", img::format::PNG);
}

Share this post


Link to post
Share on other sites
Don't throw an exception in the constructor. ;)

I generally use constructors purely to initialise the internal state of the object. I move any processing to an init() function or something similar. Constructors should always succeed.

If you really wanted to have the constructor do the loading, you can have a flag in the object to determine if the bmp is valid or not. So if the loading failed, you'd set mValid to false, and use some bmpObject.isValid() to test if the load went ok.

Share this post


Link to post
Share on other sites
Quote:
Original post by instinKt
Don't throw an exception in the constructor. ;)


Er, why?

Quote:
Constructors should always succeed.


Er, why? The actual C++ 'good practices' requirement is that A constructed object should always be in an usable state. This does not say anything about a constructor never failing—and constructors routinely fail when they cannot construct an usable object due to unexpected problems.

Quote:
If you really wanted to have the constructor do the loading, you can have a flag in the object to determine if the bmp is valid or not. So if the loading failed, you'd set mValid to false, and use some bmpObject.isValid() to test if the load went ok.


This is contrary to A constructed object should always be in an usable state. Here, you're making constructed objects potentially unusable (invalid) just so that constructors always succeed (something that few C++ programmers actually expect).

Make the class simpler in the cases where it works (no validity checking, no initialized-but-not-usable tidbits) and just throw an exception if you can't construct it.

Share this post


Link to post
Share on other sites
I agree with ToohrVyk - if a constructor can fail, then throw an exception -- don't rearrange the class so that it can't fail just for the sake of it (usually by creating a zombie object).

For this whole topic of how to use C++ "properly", I strongly recommended reading Scott Myers Effective C++ books. They're easy to read, make a lot of sense, and will get you jobs!

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by instinKt
Don't throw an exception in the constructor. ;)


Er, why?

Quote:
Constructors should always succeed.


Er, why? The actual C++ 'good practices' requirement is that A constructed object should always be in an usable state. This does not say anything about a constructor never failing—and constructors routinely fail when they cannot construct an usable object due to unexpected problems.

Quote:
If you really wanted to have the constructor do the loading, you can have a flag in the object to determine if the bmp is valid or not. So if the loading failed, you'd set mValid to false, and use some bmpObject.isValid() to test if the load went ok.


This is contrary to A constructed object should always be in an usable state. Here, you're making constructed objects potentially unusable (invalid) just so that constructors always succeed (something that few C++ programmers actually expect).

Make the class simpler in the cases where it works (no validity checking, no initialized-but-not-usable tidbits) and just throw an exception if you can't construct it.


A long time ago, (I've been trying to remember when and exactly what I was working on, but I seem to have forgotten everything) I was working up some nice code with a heap of new classes that were all doing processing in the constructor and throwing exceptions if problems came up.

Now I don't remember if I was having some unexpected issues, or if someone peered over my shoulder and said "don't do that", referring to the exceptions in the constructor. It had something to do with the fact that the memory would be allocated, but your "handle" to the object would not be assigned, because the constructor did not complete and you would end up with memory holes.

I've done some reading now and see that it's perfectly legal to throw in a constructor, which has really made me curious as to why I was told/found out otherwise. I found a hint that it may have once been true?

Share this post


Link to post
Share on other sites
The destructor of an object doesn't get run if the constructor throws, but that's a reason to design the constructor properly - not a reason to avoid throwing from constructors.

Share this post


Link to post
Share on other sites
I personally like to do file-loading through factory methods like so:


class Bitmap
{
private:
Color* pixels;
public:
static Bitmap* FromFile(std::string& fileName);

Bitmap(int width, int height);
~Bitmap();
};


you'd then use it like this


Bitmap* img = Bitmap::FromFile("c:/images/puppy.bmp");

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!