C ++ Summary of precautions related to "class" (12): initialization by members and assignment by Members

Source: Internet
Author: User

1. initialize by members (related to constructor and copy constructor) 

 

Use one class object to initialize another class object, for example:

Account oldacct ("Anna Livia plurabelle ");
Account newacct (oldacct );

It is called default memberwise initialization. The default value is because it occurs automatically. Whether or not we provide an explicit constructor, the reason is that the initialized unit is a single non-static data member, rather than the bitwise copy of the entire class object.

 

For example, the first definition of the account class:
Class account {
Public:
//...
 
PRIVATE:
Char * _ name;
Unsigned int _ acct_nmbr;
Double _ balance;
};

The default account copy constructor is defined as follows:

Inline account ::
Account (const account & RHs)
{
_ Name = RHS. _ name;
_ Acct_nmbr = RHS. _ acct_nmbr;
_ Balance = RHS. _ balance;
}

Using a class object to initialize this class and another object occurs in the followingProgramCase:


1. Use a class object to explicitly initialize another class object, for example:
Account newacct (oldacct );

2. Pass a class object as a real parameter to a function, for example:
Extern bool cash_on_hand (account ACCT );
If (cash_on_hand (oldacct ))
//...

Pass back a class object as the return value of a function, for example:

Extern account
Consolidate_accts (const vector <account> &)
{
Account final_acct;
 
// Do the finances...
 
Return final_acct;
}

3. Non-empty sequence container type definition, for example:
// Five string copy constructors are called
Vector <string> SVEC (5 );
(In this example, use the string default constructor to create a temporary object, and then use the string copy constructor. The temporary object is copied to the five elements of the vector in sequence .)

4. Insert a class object to a container type, for example:
SVEC. push_back (string ("Pooh "));

For most actual class definitions, considering the class security and usage correctness, it is not enough to initialize by members by default, the most common case is that the data member of a class is a pointer to the heap memory, and this memory will be deleted by the class's destructor, just like the _ name Member in the account class.

Newacct. _ name and oldacct. _ name points to the same C-style string. If oldacct leaves the domain and the account destructor is applied to it, newacct. _ name now points to a deleted memory zone. Another case is that if newacct modifies the string pointed to by _ name, oldacct will also be affected, it is difficult to track such pointing errors.

 
One solution to the "alias (aliasing) Problem" is to allocate the second copy of the string and initialize newacct. _ name to point to this new copy. To achieve this, we must change the default initialization of the account class by members, we do this by providing an explicit copy constructor.

The internal semantics of the class may also make default initialization by Members invalid. As explained above, there cannot be two account class objects holding the same account. To ensure this, we must change the default account class initialization by members. The following is the copy constructor that solves these two problems:

 

Inline account ::
Account (const account & RHs)
{
// Handle pointer alias issues
_ Name = new char [strlen (RHS. _ name) + 1];
Strcpy (_ name, RHS. _ name );
 
// Handle account uniqueness issues
_ Acct_nmbr = get_unique_acct_nmbr ();
 
// OK: copy by Members
_ Balance = RHS. _ balance;
}

 

In addition to providing the copy constructor, another alternative solution is that initialization by members is not allowed at all. This can be achieved through the following two steps:
1. Declare the copy constructor as private, which prevents the initialization by Members from happening anywhere in the Program (except for the member functions and Friends of the class ).
2. We intentionally do not provide a definition. However, we still need the Declaration in step 2 to prevent initialization by members in the class member functions and element. The C ++ language does not allow us to block the member functions and Friends of the class from accessing any private class member, but by not providing a definition, any action that tries to call the copy constructor is legal in the compilation system, but a link error is generated because it cannot find a resolvable definition.
For example, in order not to allow account class initialization by Members, we must declare this class as follows:

 
Class account {
Public:
Account ();
Account (const char *, double = 0.0 );
//...
PRIVATE:
Account (const account &);
//...
};

 

Ii. Initialization of member class objects

 

What will happen if we replace the _ name Declaration of the C-style string with the _ name Declaration of the string type?

By default, each member is checked by initialization. If the Member is of the built-in or composite data type, the initialization from the member to the member is executed directly. For example, in our original account class definition, because _ name is a pointer, it is directly initialized:

Newacct. _ name = oldacct. _ name;

However, the processing of member class objects is different. When we write the following statement:

Account newacct (oldacct );

These two objects are recognized as account objects. If the account class provides an explicit copy constructor, it is called to complete initialization; otherwise, the default value of the application is initialized by members; similarly, when a member class object is identified, the same process is applied recursively.

In our example, the string class provides an explicit copy constructor. by calling this copy constructor, _ name is initialized. Now we can think that the default account copy constructor is defined as follows:


Inline account ::
Account (const account & RHs)
{
_ Acct_nmbr = RHS. _ acct_nmbr;
_ Balance = RHS. _ balance;
 
// C ++ pseudoCode
// Indicates that a class member is called.
// Object copy constructor
_ Name. String: string (RHS. _ name );
}

 

By default, the account class can correctly handle the allocation and release of _ name during the member initialization process, but the copy account is still incorrect. Therefore, we still need to provide an explicit copy constructor. The following code is not very correct. Can you see why?

// Not quite right
Inline account ::
Account (const account & RHs)
{
_ Name = RHS. _ name;
_ Balance = RHS. _ balance;
_ Acct_nmbr = get_unique_acct_nmbr ();
}

 

This implementation is not completely correct because we did not initialize and assign values separately. As a result, the default string constructor is called instead of the string copy constructor. Instead, the default string constructor is called during implicit initialization, in addition, the string copy assignment operator is called in the constructor. The correction is simple:


Inline account ::
Account (const account & RHs)
: _ Name (RHS. _ name)
{
_ Balance = RHS. _ balance;
_ Acct_nmbr = get_unique_acct_nmbr ();
}

Once again, the real job is to realize at the beginning that we need to provide a correction that the results of both implementations are _ name holding RHS. the value of _ name, except that the first implementation requires repeated work twice. A general rule is to initialize all member class objects in the member initialization table.

 

3. assign values by members (related to the copy assignment operator)

 

Default Value assignment by members (default memberwise assignment) is used to assign values to another object of the class. The Mechanism is basically the same as that of default value assignment by members; however, it uses an implicit copy assignment operator to replace the copy constructor. For example:


Newacct = oldacct;

By default, the values of corresponding members of oldacct are assigned to each non-static member of newacct in sequence. in concept, it is as if the compiler has generated the following copy assignment operators:


Inline account &
Account ::
Operator = (const account & RHs)
{
_ Name = RHS. _ name;
_ Balance = RHS. _ balance;

_ Acct_nmbr = RHS. _ acct_nmbr;
}

In general, if the default initialization by members is not suitable for a class, the default assignment by members is also not suitable. For example, for the definition of the original account class, _ name is declared as char * type _ name and _ acct_nmbr are not suitable for assigning values by members.
By providing an explicit copy assignment operator instance, you can change the default value assignment by member. In this operator instance, we implement the correct class copy semantics, the general form of the copy assignment operator is as follows:

 

// Copy the normal form of the value assignment operator
Classname &
Classname ::
Operator = (const classname & RHs)
{
// Ensure that no self-copy is performed
If (this! = & RHs)
{
// The class copy semantics is here
}
 
// Return the assigned object
Return * this;
}

Here, the conditional test is:
If (this! = & RHs)

You should prevent a class object from assigning values to yourself, because (first release the resources related to the object so as to allocate resources related to the object to be copied) this copy assignment operator is especially inappropriate for copying itself. For example, consider the account copy assignment operator:

Account &
Account ::
Operator = (const account & RHs)
{
// Avoid assigning values to itself
If (this! = & RHs)
{
Delete [] _ name;
_ Name = new char [strlen (RHS. _ name) + 1];
Strcpy (_ name, RHS. _ name );
_ Balance = RHS. _ balance;
_ Acct_nmbr = RHS. _ acct_nmbr;
}
Return * this;
}

When a class object is assigned to another class object, such

Newacct = oldacct;

The following steps will occur:

1. Check this class to see if it provides an explicit copy assignment operator;
2. If yes, check the access permission to determine whether it can be called in this program;

3. if it cannot be called, a compilation time error is generated. Otherwise, it is called to perform the value assignment operation;
4. If the class does not provide an explicit copy assignment operator, assign values by members by default;
5 by default, each data member of the built-in or composite type is assigned to the corresponding member;
6 for each class member object, perform 1 to 6 steps recursively until all built-in or composite data members are assigned values.


For example, if we modify the definition of the account class again to make _ name a member Class Object of the string type, then:

 
Newacct = oldacct;

The default value assignment is called, as if the compiler generates the following copy assignment operator for us:

Inline account &
Account ::
Operator = (const account & RHs)
{
_ Balance = RHS. _ balance;
_ Acct_nmbr = RHS. _ acct_nmbr;
 
// Even at the programmer level,
// This call is correct.
// Equivalent to the short form: _ name = RHS. _ name
_ Name. String: Operator = (RHS. _ name );
}

However, it is still not suitable to assign values by members by default for account objects, and members of _ acct_nmbr are also copied by Members. We still need to provide an explicit copy assignment operator, however, it uses the string object of the member class to process name:

 

Account &
Account ::
Operator = (const account & RHs)
{
// Avoid assigning values to the Class Object
If (this! = & RHs)
{
// Call String: Operator = (const string &)
_ Name = RHS. _ name;
_ Balance = RHS. _ balance;
}
 
Return * this;
}

If you want to completely prohibit copying by Members, you need to declare the operator as private and do not provide the actual definition, just like forbidding initialization by members.


In general, the copy constructor and the copy assignment operator should be considered as an individual unit, because when we need one of them, we often need another one. When we try to deny one, you may also need to disable another one.

Related Article

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.