C ++ Primer study note _ 57 _ class and data abstraction-manage pointer members

Source: Internet
Author: User

Replication control-manage pointer members



Introduction:

For classes that contain pointers, pay special attention to the replication control. The reason is that the address in the pointer is copied only when the pointer is copied, and the object pointed to by the pointer is not copied!

When copying a pointer to another pointer, the two pointers point to the same object. When two pointers point to the same object,Any pointer may be used to change the basic object.. Similarly, when a pointer deletes an object, the user of the other pointer still thinks that the basic object still exists. By default, pointer members have the same behavior as pointer objects.

Most C ++ classes use one of the following three methods to manage pointer members:

1)Pointer members adopt regular pointer Behavior: This class has all pointer defects, but does not require special replication control!

2)Class can realize the so-called "smart pointer" behavior: The Pointer Points to an object that is shared, but the class can prevent the suspension pointer.

3)Class adopts value-type behavior: The Pointer Points to an object that is unique and managed independently by each class object.


I. Define regular pointer classes

1. A pointer class with pointer members

class HasPtr{public:    HasPtr(int *p,int i):ptr(p),val(i) {}    int *get_ptr() const    {        return ptr;    }    int get_val() const    {        return val;    }    void set_ptr(int *p)    {        ptr = p;    }    void set_val(int i)    {        val = i;    }    int get_ptr_val()   const    {        return *ptr;    }    void set_ptr_val(int i) const    {        *ptr = i;    }private:    int *ptr;    int val;};


2. Default copy/assign value and pointer member

Because the HasPtr class does not define the replication constructor, copying a HasPtr object will copy two members:

    int obj = 0;    HasPtr ptr1(&obj,42);    HasPtr ptr2(ptr1);

After copying, the int value is clear and independent, but the pointer is entangled!

[Careful mine]

Classes that have pointer members and use the default synthetic copy constructor have all the defects of common pointers. Especially,The class itself cannot avoid hanging pointer.


3. Pointers share the same object

When copying an arithmetic value, the copy is independent of the original version. You can change one copy without changing the other:

    ptr1.set_val(0);    cout << ptr1.get_val() << endl;    cout << ptr2.get_val() << endl;

When copying a pointer, the address value can be distinguished, but the Pointer Points to the same basic object. Therefore, if set_ptr_val is called on any object, the basic objects of the two will change:

    ptr1.set_ptr_val(0);    cout << ptr1.get_ptr_val() << endl;    cout << ptr2.get_ptr_val() << endl;

When two pointers direct to the same object, any of them can change the value of the shared object.


4. PossibleSuspension pointer

Because the class directly copies the pointer, the user may face a potential problem: HasPtr stores the given pointer. UserOnlyHasPtrObject exists,The object to which the Pointer Points exists.:

Int * ip = new int (42); HasPtr ptr (ip, 42); delete ip; // this will cause the hanging pointer ptr. set_ptr_val (0); // Error, but the compiler cannot detect cout <ptr. get_ptr_val () <endl; // Error, but cannot be detected by the compiler

Any changes made to the object pointed to by the pointer will apply to the shared object. If you delete this object, the class has a DRDs pointer pointing to an object that no longer exists.

// Exercise P421 13.20 int I = 42; HasPtr p1 (& I, 42); HasPtr p2 = p1; // call the value assignment operator synthesized by the compiler // copy the two Members cout <p2.get _ ptr_val () <endl; p1.set _ ptr_val (1); cout <p2.get _ ptr_val () <endl;

Ii. Define smart pointer class [can solve the problem of suspension pointer]

Smart pointerIn addition to adding features, the behavior is the same as that of normal pointers. In this example, the smart pointer is used to delete shared objects. The user will dynamically allocate an object and pass the object address to the new HasPtr class. Users can still access objects through normal pointers,However, pointers cannot be deleted.. The HasPtr class will ensure that the object is deleted when the last HasPtr object pointing to the object is revoked.

HasPtr performs the same behavior as normal pointers in other aspects. Specifically, when copying an object, the copy object and the original object will point to the same basic object,If you use a copy to change the basic object,The value accessed through another object will also change (similar to the common pointer member in the above example).

The new HasPtr class requires a destructor to delete pointers. However, the Destructor cannot delete pointers unconditionally. If two HasPtr objects point to the same basic object,Before both objects are revoked,We do not want to delete basic objects.. To compile the destructor, you need to know whether the HasPtr object is the last one pointing to the given object.


1. Introduce usage count

The general technology used to define smart pointers is to use a count [reference count]. The smart pointer class associates a counter with the object to which the class points.Use count to track how many objects in this class share the same pointer. Delete an object when the count is 0.

[Idea :]

1)Each timeCreate a new object of the classHour,Initialize the pointer andSet the count1.

2) when an object is created as a copy of another object, copy the constructor to copy the pointer and add the corresponding Count value.

3) when an object is assigned a value, the value assignment operator reduces the Count value of the object indicated by the left operand (if the count is reduced to 0, the object is deleted ), add the Count value of the object indicated by the right operand.

4) At last, when calling the destructor, The Destructor reduces the Count value,If the count is reduced0,Delete the basic object..

The only innovation lies in deciding where to put the use count. The counter cannot be directly placed in the HasPtr object:

    int obj;    HasPtr p1(&obj,42);    HasPtr p2(p1);    HasPtr p3(p2);

If the count is saved in the HasPtr object, how can I update it when p3 is created? The count increment can be copied to p3 in p1, but how can I update the count in p2?


2. Use the Count class

Define a separate specific class to encapsulate the use of counting and related pointers:

Class U_Ptr {// set HasPtr to a friend Meta class so that its members can access the member friend class HasPtr of U_Ptr; int * ip; size_t use; U_Ptr (int * p ): ip (p), use (1 ){}~ U_Ptr () {delete ip ;}};

Set all Members to private: we do not want normal users to use the U_Ptr class, so they do not have any public members!


The U_Ptr class saves pointers and uses counts. Each HasPtr object points to a U_Ptr object and uses counts to track the number of HasPtr objects that point to each U_Ptr object. U_Ptr defines only the number of constructor and destructor. The constructor copies the pointer, And the Destructor deletes it. The constructor also sets the count to 1, indicating that a HasPtr object points to this U_Ptr object.
Assuming that a HasPtr object is just created from a pointer pointing to the int value 42, these objects are:


If you copy this object, the object is:


3. Use of the counting class

The new HasPtr class saves a pointer to the U_Ptr object. The U_Ptr object points to the actual int base object:

class HasPtr{public:    HasPtr(int *p,int i):ptr(new U_Ptr(p)),val(i){}    HasPtr(const HasPtr &orig):ptr(orig.ptr),val(orig.val)    {        ++ ptr->use;    }    HasPtr &operator=(const HasPtr &orig);    ~HasPtr()    {        if ( -- ptr -> use == 0 )        {            delete ptr;        }    }private:    U_Ptr *ptr;    int val;};

The HasPtr constructor that accepts a pointer and an int value creates a U_Ptr object using its pointer parameter. After the HasPtr constructor is executed, the HasPtr object points to a newly allocated U_Ptr object, which stores the given pointer. The use count in the new U_Ptr is 1, indicating that only one HasPtr object points to it.

Copy the constructor to copy members from the form parameters and increase the Count value. After the copy constructor is executed, the newly created object points to the same U_Ptr object as the original object, and the usage count of this U_Ptr object is increased by 1.

The Destructor checks the usage count of the basic U_Ptr object. If the count is 0, this is the last HasPtr object pointing to this U_Ptr object. In this case, the HasPtr destructor deletes its U_Ptr pointer. Deleting this pointer will call the U_Ptr destructor, And the U_Ptr destructor will delete the basic int object.


4. Value assignment and usage count

The value assignment operator is a little more complex than the copy constructor:

HasPtr &HasPtr::operator=(const HasPtr &rhs){    ++ rhs.ptr -> use;    if ( -- ptr -> use == 0)        delete ptr;    ptr = rhs.ptr;    val = rhs.val;    return *this;}

Here, we first add 1 to the count in the right operand, then subtract 1 from the count in the left operand object, and check the count. As in the destructor, if this is the last object pointing to the U_Ptr object, delete the object,This will be revoked in turnIntBasic Object. After the current value in the left operand is reduced by 1 (this object may be revoked), the pointer is copied from rhs to this object.

This value assignment operator adds 1 to rhs's count before reducing the Count used by the left operand, thus preventing the assignment by itself. If the left and right operands are the same, the effect of the value assignment operator is that the count of the basic U_Ptr object is increased by 1 and then immediately reduced by 1.


5. Change other members

class HasPtr{public:    int *get_ptr() const    {        return ptr -> ip;    }    int get_val() const    {        return val;    }    void set_ptr(int *p)    {        ptr -> ip = p;    }    void set_val(int i)    {        val = i;    }    int get_ptr_val() const    {        return *(ptr -> ip);        // or return * ptr->ip;    }    void set_ptr_val(int i)    {        * ptr-> ip = i;    }private:    U_Ptr *ptr;    int val;};

When the HasPtr object is copied, the pointer in the copy object and the original object still point to the same basic object. Changes to the basic object will affect the value seen through any HasPtr object. However, HasPtr users do not have to worry about the suspension pointer. As long as they let the HasPtr class release objects, the HasPtr class will ensure that as long as there is a HasPtr object pointing to the basic object, the basic object will exist.


[Suggestion: The management pointer member P425 is worth reading carefully]

Objects with pointer members generally need to define replication control members.If you depend on the merged version,This will increase the burden on the class users. The user must ensure that the object to which the Member points exists.,As long as there is an object pointing to this object.

To manage classes with pointer members, you must define three replication control members: copy constructor, assign value operator, and destructor. These members can define pointer-type behavior or value-type behavior of pointer members.

The value type class returns a copy of the base value referred to by the pointer member to each object. The copy constructor allocates new elements and copies values from the Copied object. The value assignment operator removes the saved original object and copies the value from the right operand to the left operand. The Destructor revokes the object.

Another choice for defining value-type behaviors or pointer-type behaviors is to use some classes called "smart Pointers. These classes share the same basic value among objects to provide pointer-type behavior. However, they use replication Control Technology to avoid some defects of regular pointers. To implement smart pointer behavior, the class must ensure that the basic object persists until the last copy disappears. Counting is a common technology used to manage smart pointer classes.

These methods for managing pointers are frequently used.,ThereforeProgrammers with pointer member classes must be fully familiar with these programming technologies..


// Exercise P425 13.24 class U_Ptr {friend class HasPtr; int * ip; size_t use; U_Ptr (int * p): ip (p), use (1 ){}~ U_Ptr () {delete ip ;}}; class HasPtr {public: HasPtr (int * p, int I): ptr (new U_Ptr (p), val (I) {} HasPtr (const HasPtr & orig): ptr (orig. ptr), val (orig. val) {++ ptr-> use;} HasPtr & operator = (const HasPtr &);~ HasPtr () {if (-- ptr-> use = 0) delete ptr;} int * get_ptr () const {return ptr-> ip;} int get_int () const {return val;} void set_ptr (int * p) {ptr-> ip = p;} void set_int (int I) {val = I;} int get_ptr_val () const {return * ptr-> ip;} void set_ptr_val (int I) {* ptr-> ip = I;} private: U_Ptr * ptr; int val ;}; hasPtr & HasPtr: operator = (const HasPtr & rhs) {++ rhs. ptr-> use; if (-- ptr-> use = 0) delete ptr; ptr = rhs. ptr; val = rhs. val; return * this ;}

Iii. Define Value Type

When copying a value-type object, you will get a different new copy. Changes made to the copy will not be reflected in the original object, and vice versa. (Similar to string)

Class HasPtr {private: HasPtr (const int & p, int I): ptr (new int (p), val (I) {} // replication control HasPtr (const HasPtr & rhs): ptr (new int (* rhs. ptr), val (rhs. val) {} HasPtr & operator = (const HasPtr & rhs );~ HasPtr () {delete ptr;} int * get_ptr () const {return ptr;} int get_val () const {return val;} void set_ptr (int * p) {ptr = p;} void set_val (int I) {val = I;} int get_ptr_val () const {return * ptr;} void set_ptr_val (int I) const {* ptr = I;} public: int * ptr; int val ;};

The copy constructor no longer copies pointers. It will allocate a new int object and initialize the object to save the same value as the Copied object. Each object stores different copies of its own int value. Because each object saves its own copy,Therefore, the Destructor will unconditionally Delete the pointer..

Therefore, the value assignment operator does not need to assign new objects. Instead, it must remember to assign new values to the objects to which the Pointer Points, rather than assigning values to the pointer itself:

HasPtr &HasPtr::operator=(const HasPtr &rhs){    *ptr = *rhs.ptr;    val = rhs.val;    return *this;}

Even if you want to assign an object to itself, the value assignment operator must always be correct. In this example, operations are essentially safe even if the Left and Right operands are the same. Therefore, you do not need to explicitly check your own assignment.


// Exercise P427, exercise 13.26, and exercise 27 // refer to the previous Code and analysis. I will not go into details here. O (strong _ strong) O thank you.


// Exercise 13.28 // (1) class TreeNode {public: TreeNode (): count (0), left (0), right (0) {} TreeNode (const TreeNode & node): value (node. value), count (node. count) {if (node. left) {left = new TreeNode (* node. left);} else {left = 0;} if (node. right) {right = new TreeNode (* node. right);} else {right = 0 ;}}~ TreeNode () {if (left) delete left; if (right) delete right;} private: std: string value; int count; TreeNode * left; TreeNode * right ;};

//(2)class BinStrTree{public:    BinStrTree():root(0) {}    BinStrTree(const BinStrTree &node)    {        if (node.root)            root = new TreeNode(*node.root);        else            root = 0;    }    ~BinStrTree()    {        if (root)            delete root;    }private:    TreeNode *root;};


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.