C + + hands teach you how to implement variable length arrays

01 implement custom variable length array type

Suppose we want to implement an array that will be automatically expanded. What functions should we implement? First, from the implementation given by the main function below, we can see what functions we need to implement.

int main()
{
    MyArray a;  // Initialized array is empty
    for(int i = 0; i < 5; ++i)
        a.push_back(i); // Push back is a member function
        
    MyArray a2,a3;
    a2 = a; // Overloaded assignment operator function
    
    // Since the previous a2 = a statement, a.length() is actually a2.length()
    for(int i = 0; i < a.length(); ++i) 
        cout << a2[i] << " ";
    
    a2 = a3; // a2 is an empty array
    for(int i = 0; i < a2.length(); ++i) // a2.length() returns 0
        cout << a2[i] << " ";
    cout << endl;
    
    a[3] = 100;    // Overloaded [] operator function
    MyArray a4(a); // Overload copy constructor
    
    for(int i = 0; i < a4.length(); ++i)
        cout << a4[i] << " ";
    
    return 0;
}

Output results:

0 1 2 3 4
0 1 2 100 4

What should we do to achieve this? Let me list:

  • To store array elements in dynamically allocated memory, you need a pointer member variable
  • Overloaded assignment = operator
  • Overload [] operator
  • Overload copy constructor
  • Implement the push back and length() functions

02 implementation steps of myArray class

To implement a variable length array class, you need to implement the following seven functions:

class MyArray // Variable length array class
{
public:
    // 1. Constructor, s represents the number of array elements
    MyArray(int s = 0);
    
    // 2. Copy constructor
    MyArray(MyArray &a);
    
    // 3. Destructor
    ~MyArray();
    
    // 4. Overload assignment = operator function, used for assignment between array objects
    MyArray & operator=(const MyArray & a);

    // 5. Overload [] operator function to get the value of array subscript pair
    int & operator[](int i);
    
    // 6. Add an element to the end of the array
    void push_back(int v);
    
    // 7. Get the length of the array
    int length();

private:
    int  m_size; // Number of array elements
    int* m_ptr;  // Array pointing to dynamic allocation
};

1. Constructor

The purpose of the constructor is to initialize an array. The code is as follows:

// Constructor
MyArray::MyArray(int s = 0):m_size(s)
{
    // When initializing an array of length 0, the array pointer is null
    if(s == 0)
        m_ptr = NULL;
    // When the initialization length is not 0, the space of corresponding size will be applied
    else
        m_ptr = new int[s];
}

2. Copy constructor

The purpose of copy constructor is to produce the same object as the input parameter object, but since MyArray class has pointer member variables, we must use the method of deep copy to implement copy constructor. If we use the default copy constructor, the pointer member variables of the two objects will point to the same address, which is very dangerous.

// copy constructor 
MyArray::MyArray(const MyArray &a)
{
    // If the pointer address of the array object of the input parameter is null, an empty array is also initialized
    if(a.m_ptr == NULL)
    {
        m_ptr = NULL;
        m_size = 0;
    }
    // If the array object of the input parameter has data, apply for a new address, and finally copy the data and size of the array object of the input parameter.
    else
    {
        m_ptr = new int[a.m_size];
        memcpy(m_ptr, a.m_ptr, sizeof(int)*a.m_size);
        m_size = a.m_size;
    }
}

3. Destructor

The purpose of destructors is to free the resources of arrays

// Destructor
MyArray::~MyArray()
{
    // Release resource if pointer address is not empty
    if(m_ptr)
        delete [] m_ptr;
}

4. Overload assignment = operator function

The purpose of overloading the assignment = operator function is to make the array stored in the object on the left of the = sign the same size and content as the object on the right

// Overloaded assignment = operator function
MyArray & MyArray::operator=(const MyArray & a)
{
    if(m_ptr == a.m_ptr) // Prevent errors caused by assignments such as a=a
        return *this; 
    
    if(a.m_ptr == NULL) // If the array in a is empty
    {
        if(m_ptr)
            delete [] m_ptr; // Freeing resources from old arrays
        
        m_ptr = NULL;
        m_size = 0;
        return *this;
    }
    
    if(m_size < a.m_size) // If the original space is large enough, no new space will be allocated
    {
        if(m_ptr)
            delete [] m_ptr; // Freeing resources from old arrays
            
        m_ptr = new int[a.m_size]; // Request new memory address
    }
    
    memcpy(m_ptr, a.m_ptr, sizeof(int)*a.m_size);
    m_size = a.m_size;
    return *this;
}

5. Overload [] operator function

The purpose of overloading the [] operator function is to get the array value of the corresponding subscript through the [] operator

// Overloaded [] operator function
int & MyArray::operator[](int i)
{
    return m_ptr[i]; // Returns the array value of the corresponding subscript
}

6. Functions adding elements to the end of the array

The purpose of the push back function is to add a new element to the end of the array

// Add an element at the end of the array
void MyArray::push_back(int v)
{
    if(m_ptr) // If the array is not empty
    {
        int *tmpPtr = new int[m_size + 1]; // Reallocate space
        memcpy(tmpPtr, m_ptr, sizeof(int)*m_size); // Copy the contents of the original array
        delect [] m_ptr;
        m_ptr = tmpPtr;
    }
    else // If the array is empty
    {
        m_ptr = new int[1];   
    }
    
    m_ptr[m_size++] = v; //Add new array elements
}

7. Get the function of array length

The length() function is relatively simple. It directly returns the member variable m_size, which is the length of the array

// Function to get the length of an array
int MyArray:;length()
{
    return m_size;
}

03 summary

The whole code of variable length array type is as follows:

class MyArray
{
public:
    // 1. Constructor, s represents the number of array elements
    MyArray(int s = 0):m_size(s)
    {
        if(s == 0)
            m_ptr = NULL;
        else
            m_ptr = new int[s];
    }
    
    // 2. Copy constructor
    MyArray(const MyArray &a)
    {
        if(a.m_ptr == NULL)
        {
            m_ptr = NULL;
            m_size = 0;
        }
        else
        {
            m_ptr = new int[a.m_size];
            memcpy(m_ptr, a.m_ptr, sizeof(int)*a.m_size); // Copy the contents of the original array
            m_size = a.m_size;
        }
    }
    
    // 3. Copy constructor
    ~MyArray()
    {
        if(m_ptr)
            delete [] m_ptr;
    }
    
    // 4. Overload assignment = operator function
    MyArray & operator=(const MyArray & a)
    {
        if(m_ptr == a.m_ptr)
            return *this;
        
        if(a.m_ptr == NULL)
        {
            if(m_ptr)
                delete [] m_ptr;
            
            m_ptr = NULL;
            m_size = 0;
            return *this;
        }
        
        if(m_size < a.m_size)
        {
            if(m_ptr)
                delete [] m_ptr;
                
            m_ptr = new int[a.m_size];
        }
        
        memcpy(m_ptr, a.m_ptr, sizeof(int)*a.m_size); // Copy the contents of the original array
        m_size = a.m_size;
        return *this;
    }
    
    // 5. Overload [] operator function
    int & operator[](int i)
    {
        return m_ptr[i];
    }
    
    // 6. Add a new element at the end of the array
    void push_back(int v)
    {
        if(m_ptr) // If the array is not empty
        {
            int *tmpPtr = new int[m_size + 1]; // Reallocate space
            memcpy(tmpPtr, m_ptr, sizeof(int)*m_size); // Copy the contents of the original array
            delete [] m_ptr;
            m_ptr = tmpPtr;
        }
        else // If the array is empty
        {
            m_ptr = new int[1];   
        }
        
        m_ptr[m_size++] = v; //Add new array elements
    }
    
    // 7. Get the length of the array
    int length()
    {
        return m_size;
    }

private:
    int  m_size; // Number of array elements
    int* m_ptr;  // Array pointing to dynamic allocation
};

In fact, the variable length array class lacks the following functions, such as the function to delete an element, the function to empty an array, etc., which can be left for you to think about.

There is also room for optimization in the push back function. Every element added to the current push back function will reallocate new memory, which will increase the cost. Then, the optimization idea is as follows:
Allocate a space of N in advance. When the array size is not enough, continue to reallocate the space of 2n, and so on.

Keywords: C++

Added by mxl on Tue, 03 Dec 2019 02:38:46 +0200