A
destructor is another special kind of class member function that is executed when an object of that class is destroyed. They are the counterpart to constructors. When a variable goes out of scope, or a dynamically allocated variable is explicitly deleted using the delete keyword, the class destructor is called (if it exists) to help clean up the class before it is removed from memory. For simple classes, a destructor is not needed because C++ will automatically clean up the memory for you. However, if you have dynamically allocated memory, or if you need to do some kind of maintenance before the class is destroyed (eg. closing a file), the destructor is the perfect place to do so.
Like constructors, destructors have specific naming rules:
1) The destructor must have the same name as the class, preceded by a tilde (~).
2) The destructor can not take arguments.
3) The destructor has no return type.
Note that rule 2 implies that only one destructor may exist per class, as there is no way to overload destructors since they can not be differentiated from each other based on arguments.
Let’s take a look at a simple string class that uses a destructor:
08 | MyString( const char *pchString= "" ) |
12 | m_nLength = strlen (pchString) + 1; |
15 | m_pchString = new char [m_nLength]; |
18 | strncpy (m_pchString, pchString, m_nLength); |
21 | m_pchString[m_nLength-1] = '\0' ; |
33 | char * GetString() { return m_pchString; } |
34 | int GetLength() { return m_nLength; } |
Let’s take a look at how this class is used:
3 | MyString cMyName( "Alex" ); |
4 | std::cout << "My name is: " << cMyName.GetString() << std::endl; |
This program produces the result:
My name is: Alex
On the first line, we instantiate a new MyString class and pass in the C-style string “Alex”. This calls the constructor, which dynamically allocates memory to hold the string being passed in. We must use dynamic allocation here because we do not know in advance how long of a string the user is going to pass in.
At the end of main(), cMyName goes out of scope. This causes the ~MyString() destructor to be called, which deletes the buffer that we allocated in the constructor!
Constructor and destructor timing
As mentioned previously, the constructor is called when an object is created, and the destructor is called when an object is destroyed. In the following example, we use cout statements inside the constructor and destructor to show this:
09 | std::cout << "Constructing Simple " << nID<< std::endl; |
15 | std::cout << "Destructing Simple" << m_nID << std::endl; |
18 | int GetID() { return m_nID; } |
25 | std::cout << cSimple.GetID() << std::endl; |
28 | Simple *pSimple = new Simple(2); |
29 | std::cout << pSimple->GetID() << std::endl; |
This program produces the following result:
Constructing Simple 1
1
Constructing Simple 2
2
Destructing Simple 2
Destructing Simple 1
Note that “Simple 1″ is destroyed after “Simple 2″ because we deleted pSimple before the end of the function, whereas cSimple was not destroyed until the end of main().
As you can see, when constructors and destructors are used together, your classes can initialize and clean up after themselves without the programmer having to do any special work! This reduces the probability of making an error, and makes classes easy to use.
No comments:
Post a Comment