C ++ proverbs: Copy all components of an object

Source: Internet
Author: User

In a well-designed object-oriented system, in order to compress the space inside the object, only two functions are left for copying objects: copy constructor) and copy assignment operator ). They are collectively referred to as copying functions ). If necessary, the compiler will generate a copy function and clarify that the versions generated by the compiler are as expected: they copy all the data of the copied objects.

When you declare your own 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: It doesn't tell you when your implementation has some errors that are almost deterministic.

Consider a class that symbolizes consumers. The copy function here 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 another 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 copy functions only make partial copies: they copy the Customer's name, but do not copy its lastTransaction. However, most compilers do not care about this, even at the highest warning level (maximal warning level ). These are retaliation for writing your own copy functions. You have rejected the copy functions they write, so if your code is incomplete, they will not tell you. Conclusion: if you add a data member to a class, you must update the copy function. (You also need to update all constructors in the class and any non-standard operator =. One of the most confusing situations about this problem is that it occurs through 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 copy function of PriorityCustomer seems to have copied everything in PriorityCustomer, but let's take a look. Yes, it does copy the data member declared by PriorityCustomer, but each PriorityCustomer also includes a copy of the data member inherited from Customer, and those data members are not copied at all! The copy constructor of PriorityCustomer does not specify the parameter of the base class constructor passed to it (that is, the Customer is not mentioned in its member initialization list). Therefore, the Customer part of the PriorityCustomer object is initialized by the Customer constructor without parameters -- use the default constructor. (If it does not exist, the Code cannot be compiled .) The constructor name and lastTransaction perform a default initialization.

The copy assignment operator of PriorityCustomer is slightly different. It will not try to change the data members of its base class in any way, so they will remain unchanged.

Whenever you want to write a copy function for a derived class, you must copy the base class at the same time. The typical features in those areas are private, so you cannot directly access them. The copy function of the derived class must call the base class functions 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 this article should be clear now. When writing a copy function, you must ensure that (1) Copy all local data members and (2) Call the appropriate copy function in all base classes.

In reality, two copy functions often have similar function bodies, which may attract you to try to avoid code duplication by calling one function and the other. The idea of avoiding code duplication is worth noting, but it is wrong to use one copy function to call another.

Using the copy assignment operator to call the copy constructor is meaningless, because you are trying to construct an existing object. This is ridiculous, and there is no syntax to support it. There is a syntax that seems to make you do this, but you can't actually do it. There is also a method that uses a roundabout method, but they will damage your objects under certain conditions. So I'm not going to show you any syntax like that. Accept this opinion unconditionally: Do not use the copy assignment operator to call the copy constructor. Try another opposite method-call the copy assignment operator using the copy constructor-which is also absurd. A constructor initializes new objects, and a value assignment operator can only be used for initialized objects. Assigning a value to an object through the constructor means doing something for an object that has not been initialized, Which is meaningful only when the object has been initialized. It's a nonsense! Do not try this way.

As a replacement, if you find that your copy constructor has similar code as the copy assignment operator, you can create a third member function for the two to call to eliminate duplicates. Such a function is private and often called init. This policy is a proven method that eliminates code duplication in the copy constructor and the copy assignment operator.

Things to Remember

· The copy function should ensure that all data members of an object and all base classes are copied.

· Do not try to implement another function based on one copy function. Instead, place the common functions in the third function for both parties to call.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.