27 second order structural model

Note: the content in the blog is mainly from "Ditai Software College", and the blog is only used for private notes.

Test environment: Ubuntu 10.10

GCC version: 9.2.0

 

1, Review of constructors

1) About constructors

- class constructor for object initialization

- the constructor has the same name as the class and has no return value

- the constructor is automatically called when the object is defined

 

2, Question

1. How to judge the execution result of the constructor?

2. What happens when the return statement is executed in the constructor?

3. Does the end of constructor execution mean that the object construction is successful?

Programming experiment
 Exception constructor
27-1.cpp
#include <stdio.h>

class Test
{
    int mi;
    int mj;
    bool mStatus;
public:
    Test(int i, int j) : mStatus(false)
    {
        mi = i;
        
        return;    //The subsequent content initialization failed
        
        mj = j;    //mj becomes a random number. return terminated the function ahead of time, resulting in mj not being initialized
        
        mStatus = true;    //Force constructor to have return value
    }
    int getI()
    {
        return mi;
    }
    int getJ()
    {
        return mj;
    }
    int status()
    {
        return mStatus;
    }
};

int main()
{  
    Test t1(1, 2);
    
    if( t1.status() )    //Manual call
    {
        printf("t1.mi = %d\n", t1.getI());
        printf("t1.mj = %d\n", t1.getJ());   
    }
    
    return 0;
}

Operation:

1) g++ 27-1.cpp -o 27-1.out compilation is normal, and the print result is:

Nothing printed!

analysis:

Object construction failed, unable to print content.

 

3, The truth you should know

1) Constructor

- only the opportunity to automatically initialize member variables is provided

- there is no guarantee that the initialization logic will succeed

- the constructor ends immediately after the return statement is executed

 

4, The meaning of truth

Constructor can only determine the initial state of the object, not the birth of the object!!

 

5, Semi finished product object

1) Concept of semi-finished product object

- an object that is the result of an initialization operation that cannot be completed as expected

- semi finished objects are legal C + + objects and an important source of bugs

Programming experiment
 Hazards of semi-finished products
IntArray.h
#ifndef _INTARRAY_H_
#define _INTARRAY_H_

class IntArray
{
private:
    int m_length;
    int* m_pointer;
public: 
    IntArray(int len);
    IntArray(const IntArray& obj); 
    int length();
    bool get(int index, int& value);
    bool set(int index, int value);
    ~IntArray();
};

#endif

IntArray.cpp
#include "IntArray.h"

IntArray::IntArray(int len)
{
    m_pointer = 0;    //m_pointer is empty, object creation failed
    
    if(m_pointer)       
        for(int i=0;i<len;i++)
        {
            m_pointer[i] = 0;
        }
    }                   
    m_length = len;
}
 
IntArray::IntArray(const IntArray& obj)
{
    m_length = obj.m_length;

    m_pointer = new int[obj.m_length];
    
    for(int i=0; i<obj.m_length; i++)
    {
        m_pointer[i] = obj.m_pointer[i];
    }
}
 
int IntArray::length()
{
    return m_length;
}
 
bool IntArray::get(int index, int& value)
{
    bool ret = (0 <= index) && (index < length());
 
    if(ret)
    {
        value = m_pointer[index];
    }
 
    return ret;
}
 
bool IntArray::set(int index, int value)
{
    bool ret = (0 <= index) && (index < length());

    if( ret )
    {
        m_pointer[index] = value;
    }
    
    return ret;
}

IntArray::~IntArray()
{
    delete[]m_pointer;
}

main.cpp
#include<stdio.h>
#include "IntArray.h"

int main()
{
    IntArray a(5);
 
    printf("a.length = %d\n",a.length());   

    a.set(0,1); 
 
    return 0;
}

Operation:

1) g++ main.cpp IntArray.cpp -o IntArray.out compilation is normal, operation error:

a.length = 5
Segmentation fault (core dumped)    

Object creation failed with segment error.

 

6, Second order construction (this idea may also be used elsewhere)

1) The construction process in engineering development can be divided into

- resource independent initialization

Operations where exceptions are unlikely to occur

- operations requiring system resources

Exceptions may occur, such as memory application and file access

 

2) Second order construction example 1

class TwoPhaseCons
{
private:
    TwoPhaseCons()   //The first stage constructor: C + + constructor private attribute
    {                //The first stage will never report errors, and the program is controllable
    }
    bool construct()  //The second stage: private properties of ordinary member functions
    {                 //Resource allocation from the system is uncontrollable. Resource allocation needs to monitor whether the resource is successful
        return true;
    }
public:
    static TwoPhaseCons* NewInstance();   //Object creation function static member function
} 

 

2) Second order construction example II

TwoPhaseCons* TwoPhaseCons::NewInstance()
{
    TwoPhaseCons* ret = new TwoPhaseCons();  //Create objects in heap space
                         //Static member functions access constructors (creating objects calls private constructors)

    //If the second stage construction fails and returns NULL, whether the resource will be applied twice depends on the bool construct function
    if(!(ret && ret->construct()))
    {
         delete ret;
         ret = NULL;
    }

    return ret;
}

 

Programming experiment
 Preliminary study on second order structure
27-2.cpp
#include <stdio.h>

class TwoPhaseCons 
{
private:
    TwoPhaseCons()    //Stage 1: constructor
    {              
    }
    bool construct()  //The second stage: allocate memory, etc
    { 
        return true;  
        //Revision 2: return false;
    }
public:
    static TwoPhaseCons* NewInstance(); // Object to create a function. instance: Example
};

TwoPhaseCons* TwoPhaseCons::NewInstance() 
{
    TwoPhaseCons* ret = new TwoPhaseCons(); //Constructors can be called in member functions

    // If the second stage construction fails, NULL is returned    
    if( !(ret && ret->construct()) ) 
    {
        delete ret;
        ret = NULL;
    }
        
    return ret;
}


int main()
{
    TwoPhaseCons* obj = TwoPhaseCons::NewInstance(); //Call the static member function,

    printf("obj = %p\n", obj);

    delete obj;
    
    return 0;
}

Operation:

1) g++ 27-2.cpp -o 27-2.out compilation is normal, and the print result is:

obj = 0x8355008

2) Modification code:

int main()
{
    TwoPhaseCons* obj = new TwoPhaseCons();  //You will be prompted to call the private constructor in error
    
    printf("obj = %p\n", obj);

    delete obj;
    
    return 0;
}

g++ 27-2.cpp -o 27-2.out compilation error:

27-2.cpp: In function 'int main()':
27-2.cpp:6:2: error: 'TwoPhaseCons:TwoPhaseCons()' is private TwoPhaseCons()
Error: TwoPhaseCons::TwoPhaseCons()Constructor is private and cannot be called automatically when an object is created.    

analysis:

The constructor is private. When creating an object, the constructor cannot be called automatically to complete the object creation.

 

3) After restoring the code, modify the code, and the second stage construction returns false:

#include <stdio.h>

class TwoPhaseCons 
{
private:
    TwoPhaseCons()    //Stage 1: constructor
    {              
    }
    bool construct()  //The second stage: allocate memory, etc
    {  
        return false;    //Modify code
    }
public:
    static TwoPhaseCons* NewInstance(); // Object to create a function. instance: Example
};

TwoPhaseCons* TwoPhaseCons::NewInstance() 
{
    TwoPhaseCons* ret = new TwoPhaseCons(); //Constructors can be called in member functions

    // If the second stage construction fails, NULL is returned    
    if( !(ret && ret->construct()) ) 
    {
        delete ret;
        ret = NULL;
    }
        
    return ret;
}


int main()
{
    TwoPhaseCons* obj = TwoPhaseCons::NewInstance(); //Call the static member function,

    printf("obj = %p\n", obj);

    delete obj;
    
    return 0;
}

g++ 27-2.cpp -o 27-2.out compilation is correct, and the print result:

obj = empty

analysis:

The second stage construction failed to allocate resources in the second stage (false returned by construct()), and the object is empty after creation. If you judge whether the object address is empty, you can judge whether the object is created empty. Avoid creating failed objects and reduce the difficulty of maintenance.

 

Programming experiment
 Strengthening of array class

IntArray.h
#ifndef _INTARRAY_H_
#define _INTARRAY_H_

class IntArray
{
private:
    int m_length;
    int* m_pointer;
    
    IntArray(int len);
    IntArray(const IntArray& obj);    
    bool construct();
public:
    static IntArray* NewInstance(int length); 
    int length();
    bool get(int index, int& value);
    bool set(int index ,int value);
    ~IntArray();
};

#endif

IntArray.cpp
#include "IntArray.h"

IntArray::IntArray(int len)    //First step of second-order construction
{
    m_length = len;
}

bool IntArray::construct()    //Second step of second-order construction
{
    bool ret = true;
    
    m_pointer = new int[m_length];    //Resource request - array
    
    if( m_pointer )
    {
        for(int i=0; i<m_length; i++)
        {
            m_pointer[i] = 0;
        }
    }
    else
    {
        ret = false;
    }
    
    return ret;
}

IntArray* IntArray::NewInstance(int length) 
{
    IntArray* ret = new IntArray(length);  //Resource application - category. The first stage of second-order construction completes the basic initialization operation
    
    if( !(ret && ret->construct()) ) //Stage 2: judge whether the resource application is successful
    {
        delete ret;
        ret = 0;
    }
        
    return ret;
}

int IntArray::length()
{
    return m_length;
}

bool IntArray::get(int index, int& value)
{
    bool ret = (0 <= index) && (index < length());
    
    if( ret )
    {
        value = m_pointer[index];
    }
    
    return ret;
}

bool IntArray::set(int index, int value)
{
    bool ret = (0 <= index) && (index < length());
    
    if( ret )
    {
        m_pointer[index] = value;
    }
    
    return ret;
}

IntArray::~IntArray()
{
    delete[]m_pointer;
}

main.cpp
#include <stdio.h>
#include "IntArray.h"

int main()
{
    IntArray* a = IntArray::NewInstance(5);    
    
    printf("a.length = %d\n", a->length());
    
    a->set(0, 1);  //Set a[0]=1
    
    for(int i=0; i<a->length(); i++)
    {
        int v = 0;
        
        a->get(i, v);
        
        printf("a[%d] = %d\n", i, v); //Here's a[] just for easy reading
    }
    
    delete a;
    
    return 0;
}
a.length = 5
a[0] = 1
a[1] = 0
a[2] = 0
a[3] = 0
a[4] = 0

Summary

1) Constructors can only determine the initialization state of an object

2) The failure of initialization operation in constructor does not affect the birth of object

3) Incomplete initialization of semi-finished objects is an important source of bugs

4) The second-order construction considers that the initialization process is divided into two parts

5) The second-order construction can ensure that the created objects are fully initialized

 

Tags: C++

Posted by Impact on Fri, 20 May 2022 15:42:52 +0300