This is a note about the methods that C ++ automatically creates, even if you haven’t created them.
Who is this article for? I hope that it will be interesting to novice C ++ programmers. And experienced programmers will once again refresh and systematize their knowledge.
So, if you wrote
')
class Empty {};
then, you know that in fact you have created something like this:
class Empty {
public:
// Constructor without parameters
Empty ();
// Copy Constructor
Empty (const Empty &);
// Destructor
~ Empty ();
// Assignment operator
Empty & operator = (const Empty &);
// Operator get the address
Empty * operator & ();
// Operator get the address of a constant object
const Empty * operator & () const;
};
We must remember that these functions are always created, and can lead to unexpected results.
How can this lead to trouble?
The most annoying thing is when the program works, but not as much as you wanted. In this case, no errors in terms of language does not occur. Implicitly created methods lead precisely to such troubles.
Example One: Constructors
Consider a class that reports the creation / deletion of objects and maintains a static object counter (for simplicity, in the form of a public int).
class CC {
public:
CC ();
~ CC ();
static int cnt;
};
The implementation is trivial:
int CC :: cnt (0);
CC :: CC () {cnt ++; cout << "create \ n";}
CC :: ~ CC () {cnt--; cout << "destroy \ n";
What will such a program do?
void f (cc o) {}
int main () {
CC o;
cout << "cnt =" << o.cnt << "\ n";
f (o);
cout << "cnt =" << o.cnt << "\ n";
f (o);
cout << "cnt =" << o.cnt << "\ n";
return 0;
}
The result may surprise an unprepared reader:
create
cnt = 1
destroy
cnt = 0
destroy
cnt = -1
destroy
One gets the feeling that the object was created only once, and deleted - three times. The object counter goes to minus. At the same time, the program quietly fulfills and never falls.
As you understand, this happened because we did not consider the automatically created copy constructor, which only copies, but does not print anything and does not adjust the counter.
We can correct this situation if we add the copy constructor ourselves.
CC :: CC (const CC &) {
cnt ++; cout << "create (copy) \ n";
}
Now we get an absolutely reasonable result:
create
cnt = 1
create (copy)
destroy
cnt = 1
create (copy)
destroy
cnt = 1
destroy
Similar ambushes occur during assignment (operator =); but...
Example Two: Getting an Address
... most exquisite dirty tricks may arise if you implemented a non-trivial method of obtaining an address
CC * operator & ();
but they forgot to implement his twin, which has the same (or other?) non-trivial properties:
const CC * operator & () const;
While your program is limited to non-constant objects:
SS o;
CC * p;
p = & o;
everything is working. This can go on for a very long time, everyone will already forget how the CC object is arranged, imbued with trust and will not think about it when errors occur.
But sooner or later the code will appear:
CC const o;
CC const * q = & o;
And method
CC * operator & ();
Treacherously does not work (I already wrote about congestion over const
here ).
But enough, perhaps, examples. They all have about the same meaning. How to avoid all the troubles described.
It is very easy to insure yourself against these misunderstandings!
The easiest way is to create prototypes of all methods created automatically and
not create implementations.
Then the program simply does not link and you will receive quite a reasonable message. I got this:
/var/tmp//ccGQszLd.o(.text+0x314): In function `main ':
: undefined reference to `CC :: operator & () const '
Your compiler may express a little differently.
If this method seems clumsy to you (you have all the reasons and I agree with you), then you can create “full-fledged” methods, but make them private.
Then you will also receive error messages at compile time.
count2.cpp: In function 'int main ()':
count2.cpp: 22: error: 'const CC * CC :: operator & () const' is private
count2.cpp: 37: error: within this context
Agree that this message looks somehow ... decently.
Well, the third way (the last one on the list, but not the last one by value) is to honestly implement all the necessary methods without delaying this matter indefinitely :-)