Item 12: Copy all parts of an object)
By Scott Meyers
Translator: fatalerror99 (itepub's nirvana)
Release: http://blog.csdn.net/fatalerror99/
In the well-designed object-oriented systems (object-oriented system), the internal component of the object is encapsulated. There are only two objects (object) Copying functions: it is called copy constructor (copy constructor) and copy assignment operator (copy assignment operator ). They are collectively referred toCopying Functions(Copy function ). Item 5 tells you that, if necessary, the compiler will generate copying functions (copy function) and clarify that the versions generated by the compiler are as expected: they copy objects (objects) all data.
When you declare your copying functions (copy function), you are telling the compiler that you do not like something in the default implementation. The compiler seems to be furious with this, and they will retaliate in a strange way: they don't tell you when your implementation is almost certainly wrong.
Consider a class (class) that represents MERs (customer). Here, the copying functions (copy function) is hand-written to log the calls to them:
Void logcall (const STD: string & funcname); // make a log entry
Class Customer {
Public:
...
Customer (const customer & RHs );
Customer & operator = (const customer & RHs );
...
PRIVATE:
STD: string name;
};
Customer: customer (const customer & RHs)
: Name (RHS. Name) // copy RHS's data
{
Logcall ("customer copy constructor ");
}
Customer & Customer: Operator = (const customer & RHs)
{
Logcall ("customer copy assignment operator ");
Name = RHS. Name; // copy RHS's data
Return * This; // see item 10
}
Everything here looks good, but it is actually good-until other data member (data member) is added to the customer ):
Class Date {...}; // for dates in time
Class Customer {
Public:
... // As before
PRIVATE:
STD: string name;
Date lasttransaction;
};
Here, the existing copying functions (copy function) only performsPartial copy(Partial copy): they copy the name of MERs (customer), but do not copy its lasttransaction. However, most compilers do not give a comment even at maximal warning level (the highest warning level) (see item 53. They are retaliating against copying functions (copy function. You have rejected the copying functions (copy function) they write, so even if your code is incomplete, they will not tell you. Conclusion: if you add a data member (data member) to a class, you must ensure that you have updated the copying functions (copy function ). (You must also update all Constructors (constructors) in the class (class) (see items 4 and 45) and any non-standard operator = (item 10 provides an example ). If you forget it, the compiler may not remind you .)
One of the most confusing cases of this problem is that it occurs through inheritance (inheritance. Consider:
Class prioritycustomer: Public customer{// A derived class
Public:
...
Prioritycustomer (const prioritycustomer & RHs );
Prioritycustomer & operator = (const prioritycustomer & RHs );
...
PRIVATE:
Int priority;
};
Prioritycustomer: prioritycustomer (const prioritycustomer & RHs)
: Priority (RHS. Priority)
{
Logcall ("prioritycustomer copy constructor ");
}
Prioritycustomer &
Prioritycustomer: Operator = (const prioritycustomer & RHs)
{
Logcall ("prioritycustomer copy assignment operator ");
Priority = RHS. priority;
Return * this;
}
The copying functions (copy function) of prioritycustomer seems to have copied everything in prioritycustomer, but let's take a look. Yes, it copies the data member (data member) declared by prioritycustomer, but each prioritycustomer also includes a copy of the data members (data member) inherited from customer, those data members (data members) are not copied at all! The copy constructor (copy constructor) of prioritycustomer does not specify the parameter passed to the base class Constructor (base class constructor) (that is, in its member initialization list (member initialization List) customer is not mentioned in. Therefore, the customer part of prioritycustomer object (object) is the constructor (constructor) of the customer that does not obtain the arguments (real parameter) -- default constructor (default constructor) initialization. (If it does not exist, the Code cannot be compiled .) The constructor (constructor) performs a default initialization (default initialization) for name and lasttransaction ).
The copy assignment operator (copy assignment operator) of prioritycustomer is slightly different. It will not try to change its base class data members (base class data member) in any way, so they will remain unchanged.
Whenever you want to write copying functions (copy function) for a derived class (derived class), you must copy base class parts (base class component) at the same time ). Of course, those components are generally private (private) (see item 22), so you cannot directly access them. The copying functions (copy function) of the derived class (derived class) must call the base class functions (base class function) corresponding to them ):
Prioritycustomer: prioritycustomer (const prioritycustomer & RHs)
: Customer (RHs ),// Invoke base class copy ctor
Priority (RHS. Priority)
{
Logcall ("prioritycustomer copy constructor ");
}
Prioritycustomer &
Prioritycustomer: Operator = (const prioritycustomer & RHs)
{
Logcall ("prioritycustomer copy assignment operator ");
Customer: Operator = (RHs );// Assign base class parts
Priority = RHS. priority;
Return * this;
}
The meaning of "Copy all parts" in the title of this item should be clear now. When you write a copying functions (copy function), make sure that (1) Copy all local data members (local data member) and (2) Call all base classes (base class) the appropriate copying function in ).
In fact, two copying functions (copy function) often have similar function bodies, which may attract you to try to avoid code duplication by calling one function and the other ). Your desire to avoid code duplication is worth noting, but it is wrong to use one copying functions (copy function) to call the other to implement it.
Using copy assignment operator (copy assignment operator) to call copy constructor (copy constructor) is meaningless, because you are trying to construct an existing object (object ). This is ridiculous, and there is no syntax to support it. There is a syntax that seems to allow you to do this, but you can't actually do it. There is also a syntax that uses a roundabout method to do this, but under some conditions, they will destroy your object (object ). So I'm not going to show you any syntax like that. Accept this opinion unconditionally: Do not use copy assignment operator (copy assignment operator) to call copy constructor (copy constructor ).
Try another opposite method-call copy assignment operator (copy assignment operator) using copy constructor (copy constructor)-which is also absurd. A constructor (constructor) initializes the new objects (object), while an assignment operator (Value assignment operator) can only be applied to initialized objects (object ). Using the constructor to assign a value to an object (object) assignment means to do something for a not-yet-initialized object (object not initialized, only initialized object (initialized object) makes sense. It's a nonsense! Try is prohibited.
As a replacement, if you find that your copy constructor (copy constructor) and copy assignment operator (copy assignment operator) have similar code, eliminate duplicates by creating a third-party member function (member function) for both calls. Such a function is generally private and often called init. This policy eliminates code duplication (Code duplication) Security in copy constructor and copy assignment operator (copy assignment operator) and has been proven.
Things to remember
- Copying functions (copy function) should ensure that all data members (data members) and all base class parts (base class components) of an object are copied ).
- Do not try to implement another function based on one copying functions (copy function. In place, put common functions into a third-party function for both parties to call.