Code design in C++
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!
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.
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.
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.
I guess the OO way would be more like this:
and get rid of the "bool& success", throw an exception instead.
// java like languageIStream stream = new FileStream("filename"); // MemoryStream, NetStream etcILoader 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.
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:
I would consider:
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);}
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.
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.
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.
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!
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!
Quote:Original post by ToohrVykQuote: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?
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.
I personally like to do file-loading through factory methods like so:
you'd then use it like this
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");
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement