How to dynamically allocate arrays in C++
Asked Answered
H

6

21

I know how to dynamically allocate space for an array in C. It can be done as follows:

L = (int*)malloc(mid*sizeof(int)); 

and the memory can be released by:

free(L);

How do I achieve the equivalent in C++?

Specifically, how do I use the new and delete[] keywords? Especially in the context of creating/destroying a linked list node, or creating and destroying an array whose size is given by a variable during compile time?

Hudibrastic answered 21/2, 2016 at 4:44 Comment(3)
I highly suggest looking at the index (or table of contents) of your C++ reference for "new" or "operator new" or "dynamic memory". Any good reference should tell how to allocate and deallocate memory.Psalter
Here's a plethora of StackOverflow Articles on memory allocation for C++.Psalter
For lazy searching, I have used Google for c++ dynamic memory example.Psalter
Q
38
int* L = new int[mid]; 
delete[] L;

for arrays (which is what you want) or

int* L = new int;   
delete L;

for single elements.

But it's more simple to use vector, or use smartpointers, then you don't have to worry about memory management.

std::vector<int> L(mid);

L.data() gives you access to the int[] array buffer and you can L.resize() the vector later.

auto L = std::make_unique<int[]>(mid);

L.get() gives you a pointer to the int[] array.

Quartz answered 21/2, 2016 at 4:49 Comment(0)
G
10

Following Info will be useful : Source : https://www.learncpp.com/cpp-tutorial/6-9a-dynamically-allocating-arrays/

Initializing dynamically allocated arrays

If you want to initialize a dynamically allocated array to 0, the syntax is quite simple:

int *array = new int[length]();

Prior to C++11, there was no easy way to initialize a dynamic array to a non-zero value (initializer lists only worked for fixed arrays). This means you had to loop through the array and assign element values explicitly.

int *array = new int[5];
array[0] = 9;
array[1] = 7;
array[2] = 5;
array[3] = 3;
array[4] = 1;

Super annoying!

However, starting with C++11, it’s now possible to initialize dynamic arrays using initializer lists!

int fixedArray[5] = { 9, 7, 5, 3, 1 }; // initialize a fixed array in C++03
int *array = new int[5] { 9, 7, 5, 3, 1 }; // initialize a dynamic array in C++11

Note that this syntax has no operator= between the array length and the initializer list.

For consistency, in C++11, fixed arrays can also be initialized using uniform initialization:

int fixedArray[5] { 9, 7, 5, 3, 1 }; // initialize a fixed array in C++11
char fixedArray[14] { "Hello, world!" }; // initialize a fixed array in C++11

One caveat, in C++11 you can not initialize a dynamically allocated char array from a C-style string:

char *array = new char[14] { "Hello, world!" }; // doesn't work in C++11

If you have a need to do this, dynamically allocate a std::string instead (or allocate your char array and then strcpy the string in).

Also note that dynamic arrays must be declared with an explicit length:

int fixedArray[] {1, 2, 3}; // okay: implicit array size for fixed arrays

int *dynamicArray1 = new int[] {1, 2, 3}; // not okay: implicit size for dynamic arrays

int *dynamicArray2 = new int[3] {1, 2, 3}; // okay: explicit size for dynamic arrays
Genitive answered 11/10, 2018 at 4:56 Comment(2)
How can do this with multidimensional arrays?Conventional
Same as C. Allocate an outer array, then iterate over the elements, and for each one, assign an inner array. Or std::vector<std::vector<int>>. or std::vector<std::array<int, 3>> if you know the size of the inner arrays.Gigantes
O
4

you allocate memory using the new operator and release a pointer using delete operator. Note that you can't delete normal variables, only pointers and arrays can be deleted after accomplishing their task.

int * foo;
foo = new int [5];
delete[] foo;

a complete program

#include <iostream>
#include <new>
using namespace std;

int main ()
{
  int i,n;
  int * p;
  cout << "How many numbers would you like to type? ";
  cin >> i;
  p= new (nothrow) int[i];
  if (p == nullptr)
    cout << "Error: memory could not be allocated";
  else
  {
    for (n=0; n<i; n++)
    {
      cout << "Enter number: ";
      cin >> p[n];
    }
    cout << "You have entered: ";
    for (n=0; n<i; n++)
      cout << p[n] << ", ";
    delete[] p;
  }
  return 0;
}

result

How many numbers would you like to type? 5
Enter number : 75
Enter number : 436
Enter number : 1067
Enter number : 8
Enter number : 32
You have entered: 75, 436, 1067, 8, 32,
Ovida answered 21/2, 2016 at 4:51 Comment(0)
I
2

In C++ we have the methods to allocate and de-allocate dynamic memory.The variables can be allocated dynamically by using new operator as,

type_name *variable_name = new type_name;

The arrays are nothing but just the collection of contiguous memory locations, Hence, we can dynamically allocate arrays in C++ as,

type_name *array_name = new type_name[SIZE];

and you can just use delete for freeing up the dynamically allocated space, as follows, for variables,

delete variable_name;

for arrays,

delete[] array_name;
Immure answered 21/2, 2016 at 4:56 Comment(0)
K
1

You need to be extremely careful when using raw pointers with dynamic memory but here is a simple example.

int main() {
    // Normal Pointer To Type
    int* pX = nullptr;
    pX = new int;
    *pX = 3;

    std::cout << *pX << std::endl;

    // Clean Up Memory
    delete pX;
    pX = nullptr;

    // Pointer To Array
    int* pXA = nullptr;
    pXA = new int[10]; // 40 Bytes on 32bit - Not Initialized All Values Have Garbage
    pXA = new int[10](0); // 40 Bytes on 32bit - All Values Initialized To 0.

    // Clean Up Memory To An Array Of Pointers.
    delete [] pXA;
    pXA = nullptr;

    return 0;     

} // main

To avoid memory leaks; dangling pointers, deleting memory to early etc. Try using smart pointers. They come in two varieties: shared and unique.

SomeClass.h

#ifndef SOME_CLASS_H
#define SOME_CLASS_H

class SomeClass {
private:
    int m_x;

public:
    SomeClass();
    explicit SomeClass( x = 0 );

    void setX( int x );
    int  getX() const;

private:
    SomeClass( const SomeClass& c ); // Not Implemented - Copy Constructor
    SomeClass& operator=( const SomeClass& c ); Not Implemented - Overloaded Operator=
};  // SomeClass

#endif // SOME_CLASS_H

SomeClass.cpp

#include "SomeClass.h"

// SomeClass() - Default Constructor
SomeClass::SomeClass() :
m_x( x ) {
} // SomeClass

// SomeClass() - Constructor With Default Parameter
SomeClass::SomeClass( int x ) :
m_x( x ) {
} // SomeClass

// setX()
void SomeClass::setX( int x ) {
    m_x = x;
} // setX

// getX()
void SomeClass::getX() const {
    return m_x;
} // getX

Old Way Of Using Dynamic Memory

#include <iostream>
#include "SomeClass.h"

int main() {
    // Single Dynamic Pointer
    SomeClass* pSomeClass = nullptr;

    pSomeClass = new SomeClass( 5 );

    std::cout << pSomeClass->getX() << std::endl;

    delete pSomeClass;
    pSomeClass = nullptr;

    // Dynamic Array 
    SomeClass* pSomeClasses = nullptr;
    pSomeClasses = new SomeClasses[5](); // Default Constructor Called


    for ( int i = 0; i < 5; i++ ) {
        pSomeClasses[i]->setX( i * 10 );
        std::cout << pSomeSomeClasses[i]->getX() << std::endl;
    }

    delete[] pSomeClasses;
    pSomeClasses = nullptr;  

    return 0;
} // main

The problem here is knowing when, where and why to delete memory; knowing who is responsible. If you delete the memory to manage it and the user of your code or library assumes you didn't and they delete it there is a problem since the same memory is trying to be deleted twice. If you leave it up to the user to delete it and they assumed you did and they don't you have a problem and there is a memory leak. This is where the use of smart pointers come in handy.

Smart Pointer Version

#include <iostream>
#include <memory>
#include <vector>
#include "SomeClass.h"

int main() {
    // SHARED POINTERS
    // Shared Pointers Are Used When Different Resources Need To Use The Same Memory Block
    // There Are Different Methods To Create And Initialize Shared Pointers
    auto sp1 = std::make_shared<SomeClass>( 10 );

    std::shared_ptr<SomeClass> sp2( new SomeClass( 15 ) );

    std::shared_ptr<SomeClass> sp3;
    sp3 = std::make_shared<SomeClass>( 20 );

    std::cout << "SP1: " << sp1->getX() << std::endl;
    std::cout << "SP2: " << sp2->getX() << std::endl;
    std::cout << "SP3: " << sp3->getX() << std::endl;

    // Now If you Reach The Return Of Main; These Smart Pointers Will Decrement
    // Their Reference Count & When It Reaches 0; Its Destructor Should Be
    // Called Freeing All Memory. This Is Safe, But Not Guaranteed. You Can
    // Release & Reset The Memory Your Self.

    sp1.reset();  
    sp1 = nullptr;

    sp2.reset();
    sp2 = nullptr;

    sp3.reset();
    sp3 = nullptr;

    // Need An Array Of Objects In Dynamic Memory?
    std::vector<std::shared_ptr<SomeClass>> vSomeClasses;
    vSomeClasses.push_back( std::make_shared<SomeClass>( 2 ) );
    vSomeClasses.push_back( std::make_shared<SomeClass>( 4 ) );
    vSomeClasses.push_back( std::make_shared<SomeClass>( 6 ) );

    std::vector<std::shared_ptr<SomeClass>> vSomeClasses2;    
    vSomeClasses2.push_back( std::shared_ptr<SomeClass>( new SomeClass( 3 ) ) );
    vSomeClasses2.push_back( std::shared_ptr<SomeClass>( new SomeClass( 5 ) ) );
    vSomeClasses2.push_back( std::shared_ptr<SomeClass>( new SomeClass( 7 ) ) );

    // UNIQUE POINTERS
    // Unique Pointers Are Used When Only One Resource Has Sole Ownership.
    // The Syntax Is The Same For Unique Pointers As For Shared Just Replace
    // std::shared_ptr<SomeClass> with std::unique_ptr<SomeClass> &
    // replace std::make_shared<SomeClass> with std::make_unique<SomeClass>
    // As For Release Memory It Is Basically The Same
    // The One Difference With Unique Is That It Has A Release Method Where Shared Does Not.

    auto mp1 = std::make_unique<SomeClass>( 3 );
    mp1.release();
    mp1.reset();
    mp1 = nullptr;

    // Now You Can Also Do This:
    // Create A Unique Pointer To An Array Of 5 Integers
    auto p = make_unique<int[]>( 5 );

    // Initialize The Array
    for ( int i = 0; i < 5; i++ ) {
        p[i] = i;
    }

    return 0;
} // main

Here Are Reference Links To Both Shared & Unique Pointers

https://msdn.microsoft.com/en-us/library/hh279669.aspx

https://msdn.microsoft.com/en-us/library/hh279676.aspx

Kuenlun answered 21/2, 2016 at 6:20 Comment(0)
P
0

A comment by Adarsh Raj asks for a multi-dimensional solution.

Templates may help with the memory management of native arrays.

#include <iostream>
#include <cstring>

#define CLEANUP_TEST

#ifdef CLEANUP_TEST
long long deallocCount = 0;
#endif


/**
 * Helper for Array<..>
 * n-fold dereferencing of Element Type
 * @tparam E
 * @tparam n
 */
template <typename E, int n> struct Deref
{
    typedef typename Deref<E,n-1>::T* T;
};

template <typename E> struct Deref<E,0>
{
    typedef E T;
};


/**
 * Helper for Array<..>
 * Recursive Array Allocation
 * @tparam E
 * @tparam dim
 * @tparam zeroInit
 * @tparam cleanup
 */
template <typename E, int dim, bool zeroInit, bool cleanup> struct ArrayAllocator
{
    static typename Deref<E,dim>::T alloc(int* dim0)
    {
        typename Deref<E,dim>::T res = new typename Deref<E,dim-1>::T[*dim0];
        for (int i=0; i<*dim0; i++)
        {
            res[i] = ArrayAllocator<E,dim-1,zeroInit,cleanup>::alloc(dim0+1);
        }
        return res;
    }

    static void free(typename Deref<E,dim>::T p, int* dim0)
    {
        for (int i=0; i< *dim0; i++)
        {
            ArrayAllocator<E,dim-1,zeroInit,cleanup>::free(p[i], dim0+1);
        }
    }
};

template <typename E, bool zeroInit, bool cleanup> struct ArrayAllocator<E,1,zeroInit,cleanup>
{
    static E* alloc(int* dim0)
    {
        E* res = new E[*dim0];
        if (zeroInit)
        {
            std::memset(res, 0, (*dim0)*sizeof(E));
        }
        return res;
    }

    static void free(E* p, int* dim0)
    {
        if (cleanup)
        {
            #ifdef CLEANUP_TEST
                deallocCount += *dim0;
            #endif

            std::memset(p, 0, (*dim0)*sizeof(E));
        }
        delete[] p;
    }
};




/**
 *
 * @tparam E array element type
 * @tparam dim array dimension
 * @tparam zeroInit initialize array with zero, applicable for integral E, exclusively!
 * @tparam cleanup set all elements to zero on release, applicable for integral E, exclusively! 
 *         used case: sensitive data
 */

template <typename E, int dim, bool zeroInit=false, bool cleanup=false> class Array
{
public:
    typedef typename Deref<E,dim>::T P;

private:
    P p;

    int bounds[dim];

public:
    /**
     * Constructor for undefined array
     */
    Array()
    : p(0)
    {
    }

    /**
     * Constructor with initial allocation
     * @param bounds
     */
    Array(int bounds[dim])
    : p(0)
    {
        alloc(bounds);
    }

    /**
     * Allocate array
     * @param bounds
     */
    void alloc(int bounds[dim])
    {
        if (p!=0)
        {
            throw std::runtime_error("Illegal Array State");
        }
        std::memcpy(this->bounds, bounds, sizeof(int[dim]));
        p = ArrayAllocator<E,dim,zeroInit,cleanup>::alloc(bounds);
    }

    /**
     * Functor for Array Access
     * @return
     */
    P operator ()()
    {
        return p;
    }

    /**
     * Functor for Array Access, const version
     * @return
     */
    const P operator ()() const
    {
        return p;
    }

    /**
     * Release array, involutoric
     */
    void release()
    {
        if (p!=0)
        {
            ArrayAllocator<E,dim,zeroInit,cleanup>::free(p,bounds);
            p = 0;
        }
    }

    virtual ~Array()
    {
        release();
    }
};

Test Procedure

static void assert(bool b)
{
    if (!b)
    {
        throw std::runtime_error("Validation FAILED");
    }
}

void test()
{
    int bounds[] = {400,500,6};
    Array<int,3, true, true> A0(bounds);

    Array<int,3>::P p = A0();



    for (int i=0; i<400; i++)
    {
        for (int j=0; j<500; j++)
        {
            for (int k=0; k<6; k++)
            {
                assert(p[i][j][k]==0);
            }
        }
    }
    for (int i=0; i<400; i++)
    {
        for (int j=0; j<500; j++)
        {
            for (int k=0; k<6; k++)
            {
                p[i][j][k] = i+j+k;
            }
        }
    }
    for (int i=0; i<400; i++)
    {
        for (int j=0; j<500; j++)
        {
            for (int k=0; k<6; k++)
            {
                assert (p[i][j][k] == i+j+k);
            }
        }
    }


#ifdef CLEANUP_TEST
    deallocCount = 0;
#endif

    A0.release();
    assert(A0()==0);

#ifdef CLEANUP_TEST
    assert(deallocCount==400*500*6);
#endif

}

int main()
{
    try
    {
        test();
        std::cout << "OK" << std::endl;
    }
    catch (const std::exception& x)
    {
        std::cerr << x.what() << std::endl;
    }
    return 0;
}
Pentadactyl answered 30/4 at 15:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.