I hope it's not your final post. =(
Anyway, I thought I answered this already... but okay... I'll try to explain a little further.
1) Strictly speaking, you never
need them. But you do need them if you want a properly encapsulated class.
2) They help make your classes encapsulated.
3) It's only a problem if you're trying to make encapsulated classes
Encapsulation can be a difficult concept to grasp. Basically to be encapsulated means to be "self contained" and "self managing". A class that is properly encapsulated will be next to impossible to misuse in disasterous fashions.
4)
Here's a simple example of a class that acts as an array of 50 integers.
Here's a class that is properly encapsulated:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
class Array50
{
private:
int* data;
// you can ignore these next two lines, they just make it so this class can't be copied:
// while somewhat unimportant to this example, this wouldn't be a properly encapsulated
// class without them... so I felt I had to include them
Array50(const Array50&);
Array50& operator = (const Array50&);
public:
Array50() // the constructor
{
data = new int[50]; // allocate memory for the array
}
~Array50() // the destructor
{
delete[] data; // free the memory for the array
}
// overloading the [] operator:
int& operator [] (int index)
{
return data[index];
}
};
|
Because this class is encapsulated... using it is a breeze:
1 2 3 4 5 6
|
void somefunction()
{
Array50 myarray; // constructor automatically called, memory allocated
myarray[12] = 34; // this is OK
} // destructor automatically called, memory deleted. No worry about memory leaks.
|
Here's a simpler,
somewhat encapsulated class, but doesn't use ctors or dtors to properly encapsulate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
class Array20
{
private:
int* data;
public:
void Init()
{
data = new int[20];
}
void Destroy()
{
delete[] data;
}
int& operator [] (int index)
{
return data[index];
}
};
|
Now, using this class is no longer cut and dry. Here's how you'd use the class properly:
1 2 3 4 5 6 7 8 9 10
|
void somefunction()
{
Array20 myarray;
myarray.Init(); //allocate the memory
myarray[12] = 34; // OK
myarray.Destroy(); // clean up
}
|
Note the allocation and cleanup are no longer automatic. You have to remember to do it every time you use the class. Because of that, it's very easy to misuse the class and have disasterous results:
1 2 3 4 5 6 7 8 9 10
|
void mybadcode()
{
Array20 a;
a[12] = 34; // BAD, heap corruption. Accessing memory via an uninitialized pointer
a.Destroy(); // BAD, deleting memory that was never allocated
Array20 b;
b.Init(); // OK
b.Init(); // BAD, memory leak. memory allocated in first call to init was never deleted, resulting in leak
} // BAD, another memory leak. 2nd call to Init was never properly deleted
|
Can you imagine if you had to manually prepare and cleanup after classes like string, vector, list, etc? They would be very difficult to use! All of those STL classes use constructors and destructors to properly maintain their data so that the person using the class doesn't have to worry about it.
Encapsulated classes
"just work", and are actually somewhat difficult to use improperly.. And if the classes just work, then it's much, MUCH easier to write code using those classes.