Constructors and destructors of a Class?

Started by
21 comments, last by kelcharge 18 years, 9 months ago
Once again I am using C++ and I am trying to learn how to make a class. I get everything but the constructors and destructors. I have no clue how to use them or what they are for. So if anyone has some general information about constructors and destructors that would be much appreciated?
True God of the TribunalKelchargeMy SiteMy Ugly Forums
Advertisement
The constructors and destructors, as their name imply, are used to create and destroy an instance of a class. Take the following example:

class Point {private:  int* _x;  int* _y;  int* _z; public:  Point() {    _x = new int(0);    _y = new int(0);    _z = new int(0);  }  Point(const int& x, const int& y, const int& z) {    _x = new int(x);    _y = new int(y);    _z = new int(z);  }  ~Point() {    delete x;    delete y;    delete z;  }  //Various public methods...}


Now, obviously, this particular implementation of a point is stupid since there is no need to use dynamic allocation for the members. I did it, however, to show you an area where constructors could be useful. Note that I have 2 constructors, which take either no arguments (defaults coordinates to zero) or 3 arguments specifying the coordinates.

The destructor is then used to clean the memory that was allocated dynamically during construction.

Note that if you do not provide constructors and destructors, default ones are generated. you would then use the Point class as follow:

int main() {  Point p1;  //0 parameter constructor called;  Point p2(1, 1, 1); //3 parameters constructor called;  Point* p3 = new Point(); //0 parameter constructor called;  Point* p4 = new Point(2,2,2); //3 parameter constructor called;  delete p3;  //Destructor called for p3;  delete p4;  //Destructor called for p4;  //p1 and p2 exit scope here, destructors called.}


I teleported home one night; With Ron and Sid and Meg; Ron stole Meggie's heart away; And I got Sydney's leg. <> I'm blogging, emo style
Constructors can be thought of as functions that are performed when an object is created. For example:

class Foo(){  // constructor  Foo(){cout << "Instance of Foo has been created!\n";}  // deconstructor  ~Foo(){cout << "Instance of Foo is being destroyed!\n";}  void Bar();};


When you create an instnace of Foo, that string will be put out to the console. Deconstructors are pretty much the same except they perform functions when delete is called upon the class.
Ok. You make classes for a reason, if you don't have a reason to group the data together in a logical whole, then there's no point in making a class right? I mean no one ever makes a class call FiveUnrelatedVariables. So the values of the objects variables have to be related to somehow. The constructor's job is to make sure that the values in the object meet that relationship. Some classes this is pretty easy, and the constructor doesn't do much. Other classes, the constructor needs to do more. Basically after the constructor runs, the object should be in a valid state for any of the member functions to be called.

The destructor's job is to dispose of any resources the object owns. For example, the object may have grabbed a chunk of memory or opened a file. The destructor then will either free that memory or close the file.
So if I make a class to define tiles that I am putting into a map. In the constructor of the class it should open the file to the image of the tile? How would the destructor of this then close the file?

EDIT: I am using Allegro, and I have it open the file like so.
class CTile {    public:        BITMAP tile; //this is the bitmap it is loaded on        string file;        void SetPos(int cval, int rval); //sets the values of c and r        void Displaycr(void); //just displays the values    private:        int c; //columns        int r; //rows};


Now I get what should go into the constructor,
tile = load_bitmap(file);
, but how should I make the constructor within the class. Also how should the destructor be made?
True God of the TribunalKelchargeMy SiteMy Ugly Forums
class Foo{  Foo(){/*open file*/}  ~Foo(){/*close file*/}};int main(){  ...  Foo myFoo = new Foo(); // Open the file  ...  // do what you gotta do  delete Foo; // File is closed based on the code in the deconstructor  ...}
Quote:Original post by SiCrane
... I mean no one ever makes a class call FiveUnrelatedVariables. ...


Unfortunately, people do make classes that could be named CollectionOfUnrelatedVariables. How often do we see posts that say, "I have this class that contains all my global variables ...".
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:(Modified) Original post by Ekim_Gram
class Foo{  Foo(){/*open file, initialize bitmap with all the file data, and close it*/}  ~Foo(){/*clean up bitmap*/}};int main(){  ...  Foo* myFoo = new Foo(); // on the heap  Foo bar; // on the stack  ...  // do what you gotta do  delete myFoo; // gets cleaned up  ...} // at end of scope, the "bar" gets cleaned up because it was a local.


Probably a bit more realistic this way :)

Anyway, it depends a lot on what exactly a BITMAP is, and what the relation is between tiles and the bitmap data. In particular, I really don't think "c" and "r" are rows and columns as in a *count*; they might be a row and column ID as in a *position* in the bitmap, though. A Tile object, logically, represents *one* tile. The whole set of tiles is represented by the whole set of objects, held in a container somewhere.

Here is one possible setup. It supposes that each Tile has a name indicating which bitmap file holds its image, and row and column values indicating where on the bitmap the actual Tile is. It will store the bitmaps separately, in a std::map - a logical "mapping" from the name to the file - and when a Tile is constructed or destructed, it may load or unload (and update the map accordingly) bitmap images, according to a reference count (it tracks how many Tiles currently use the image, and unloads it when all tiles using it have been destroyed). This is not tested, and deliberately not complete - I don't know exactly how you load bitmaps so I will just indicate where that is done, and you will have to figure out which headers you need to include ([google] is your friend here) and debug it.

If BITMAP does not have a default (i.e. no-argument) constructor, this will take some hacking to make it work - let me know if you run into problems.

class Tile {  // Members of a class default to private, thus my convention is to put private  // stuff first.  static std::map<std::string, std::pair<BITMAP, int> > images;  // Several things to notice here. The map is a templated type, as is the  // "pair"; read it as "map OF string TO (pair OF BITMAP AND int)".  // std::pair is just a quick way of making a struct with two data members,  // basically. Here the BITMAP is the bitmap, and the int is a "reference  // count" - the number of constructed Tile objects using this bitmap.  // Finally, the "static" means that there is one *shared* instance of the map  // for the whole class, rather than one per Tile object - this is how it  // should be.  // This design has the Tile class managing the Tile objects. I find this sort  // of design to be clean and compact; however, there are good arguments for  // factoring out the management to a separate class, too. (I just don't happen  // to agree with those arguments most of the time. ;) )  std::string myImage; // a "key" for the map.  int row, column; // position in the indicated bitmap for drawing.  // I will also need a private helper function, that will become clear later...  void dispose() {    // Decrements the reference count for the current object's BITMAP, to say    // "I am not using this bitmap any more."    std::pair<BITMAP, int>& theBitmap = images[myImage];    --theBitmap.second;    if (!theBitmap.second) { // that was the last user, so we must      // unload the image now.      cleanupBitmap(theBitmap.first);      // Warning! Don't try to invoke ~BITMAP directly yourself; it's bad mojo.      // What you probably want to do is just something like "theBitmap.first =      // BITMAP();", i.e. assign a blank BITMAP to it, and let the BITMAP       // assignment operator take care of things for you.      // And now, remove the item from the map.      images.erase(myImage);    }  }  public:  // And now, the public functions.  Tile(std::string imageName, int r, int c) : myImage(imageName), row(r), column(c) {    // Notice how I used an initializer list (google them - possibly with    // "site:gamedev.net" - if that's not familiar to you) to set up the data    // members, since they can just be copied across.    // Now, we do the magic part of possibly loading a bitmap.    // The operator[] on a map is a lookup-with-side-effect; it will return    // a default-constructed "value" (here, a pair of int and BITMAP, i.e. the    // the int will be 0 and the BITMAP will be whatever it is) if the "key" is    // not found, and otherwise returns the matching "value". In our case, if    // the key is not found, we want to insert anyway, so this is OK. (Note that    // the value is returned by non-const reference, so we can then modify the    // value in the map directly.) Note how I store to a reference, rather than    // a value, in order to preserve that modifiability over a few lines of code    // :)    std::pair<BITMAP, int>& theBitmap = images[myImage];    if (!theBitmap.second) { // the int is 0; i.e. we need to load      theBitmap.first = createBitmap(myImage);      // the createBitmap function should open the named file, do the actual      // creation, close the file (that can happen implicitly, e.g. if you use      // an ifstream object, it will close automatically at the end of scope)      // and return the BITMAP.    }    ++theBitmap.second; // In any case, there is now one more Tile using    // that image than there was before.  }  void drawSelf(int x, int y) {    // Method to draw the tile. Note we are guaranteed that the bitmap is loaded    // and its name is in the map, because that was handled in the constructor.    // So again we can just use the operator[]:    BITMAP b = images[myImage];    drawImageChunk(x, y, b, row, column); // Again, this is yours to implement  }  ~Tile() {    // The destructor. When we destruct a tile, we have to decrease that    // reference count for its image (since there's now one fewer tile kicking    // around that uses it), and unload the image if appropriate.    dispose();    // Because all the per-object data consisted of primitives (int values) and    // a std::string (which knows how to clean itself up), there is no more to    // do here. Objects containing pointers are trickier, however - normally you    // must delete (or delete[]) them if you did new (or new[], respectively)    // them in the constructor.  }  // Any time you need any of (destructor, copy constructor, assignment   // operator), you generally need all three. This is no exception :)  Tile(const Tile& other) : myImage(other.image), row(other.row), column(other.column) {    // This is a copy constructor, called when you initialize a Tile from    // another one. When that happens, we know that the map has the image loaded    // already, but we need to increment the refcount because we added another    // object using that image.    // So we just copy the members (using the initializer list again), and then:    ++images[myImage].second;  }  // It is common to implement assignment operators in terms of the copy  // constructor, whenever there is complicated memory management to do. Here,  // that's not the case, so we don't need to create a temporary like that.  Tile& operator=(const Tile& rhs) {    myImage = other.image;    row = other.row;    column = other.column;    // Since this is an already-existing object, we need to decrement the     // reference count for that old image, and possibly unload it.    dispose();    // Notice that even though dispose() contains all the code invoked in the    // destructor, I factored it out into its own function - again, calling a    // destructor manually is bad mojo. (There is one case in which that should    // be done, but it is a part of some deep magic that beginners should not    // attempt - if curious, you can look it up in the C++ FAQ Lite.)    ++images[myImage].second;  }}
I have a problem.

//Lets say within my class, in public:, I put CTile(const char* filename);//ThenCTile::CTile(const char* filename) {   tile = load_bitmap(filename);}


Shouldn't that make it load the image?
True God of the TribunalKelchargeMy SiteMy Ugly Forums
When you actually *create* a Tile, yes. Show more code and describe the problem in more detail.

This topic is closed to new replies.

Advertisement