Search

Enter your search terms

Submit search form

10.6 — Container classes

By Alex on December 14th, 2007 | last modified by Alex on January 16th, 2018

In real life, we use containers all the time. Your breakfast cereal comes in a box, the pages in your book come inside a cover and binding, and you might store any number of items in containers in your garage. Without containers, it would be extremely inconvenient to work with many of these objects. Imagine trying to read a book that didn’t have any sort of binding, or eat cereal that didn’t come in a box without using a bowl. It would be a mess. The value the container provides is largely in its ability to help organize and store items that are put inside it.

Similarly, a container class is a class designed to hold and organize multiple instances of another type (either another class, or a fundamental type). There are many different kinds of container classes, each of which has various advantages, disadvantages, and restrictions in their use. By far the most commonly used container in programming is the array, which you have already seen many examples of. Although C++ has built-in array functionality, programmers will often use an array container class (std::array or std::vector) instead because of the additional benefits they provide. Unlike built-in arrays, array container classes generally provide dynamic resizing (when elements are added or removed), remember their size when they are passed to functions, and do bounds-checking. This not only makes array container classes more convenient than normal arrays, but safer too.

Container classes typically implement a fairly standardized minimal set of functionality. Most well-defined containers will include functions that:

Create an empty container (via a constructor)

Insert a new object into the container

Remove an object from the container

Report the number of objects currently in the container

Empty the container of all objects

Provide access to the stored objects

Sort the elements (optional)

Sometimes certain container classes will omit some of this functionality. For example, arrays container classes often omit the insert and remove functions because they are slow and the class designer does not want to encourage their use.

Container classes implement a member-of relationship. For example, elements of an array are members-of (belong to) the array. Note that we’re using “member-of” in the conventional sense, not the C++ class member sense.

Types of containers

Container classes generally come in two different varieties. Value containers are compositions that store copies of the objects that they are holding (and thus are responsible for creating and destroying those copies). Reference containers are aggregations that store pointers or references to other objects (and thus are not responsible for creation or destruction of those objects).

Unlike in real life, where containers can hold whatever types of objects you put in them, in C++, containers typically only hold one type of data. For example, if you have an array of integers, it will only hold integers. Unlike some other languages, C++ generally does not allow you to mix types inside a container. If you need containers to hold integers and doubles, you will generally have to write two separate containers to do this (or use templates, which is an advanced C++ feature). Despite the restrictions on their use, containers are immensely useful, and they make programming easier, safer, and faster.

An array container class

In this example, we are going to write an integer array class from scratch that implements most of the common functionality that containers should have. This array class is going to be a value container, which will hold copies of the elements it’s organizing.

First, let’s create the IntArray.h file:

1

2

3

4

5

6

7

8

#ifndef INTARRAY_H

#define INTARRAY_H

classIntArray

{

};

#endif

Our IntArray is going to need to keep track of two values: the data itself, and the size of the array. Because we want our array to be able to change in size, we’ll have to do some dynamic allocation, which means we’ll have to use a pointer to store the data.

1

2

3

4

5

6

7

8

9

10

11

#ifndef INTARRAY_H

#define INTARRAY_H

classIntArray

{

private:

intm_length;

int*m_data;

};

#endif

Now we need to add some constructors that will allow us to create IntArrays. We are going to add two constructors: one that constructs an empty array, and one that will allow us to construct an array of a predetermined size.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

#ifndef INTARRAY_H

#define INTARRAY_H

#include <cassert> // for assert()

classIntArray

{

private:

intm_length;

int*m_data;

public:

IntArray():

m_length(0),m_data(nullptr)

{

}

IntArray(intlength):

m_length(length)

{

assert(length>=0);

if(length>0)

m_data=newint[length];

else

m_data=nullptr;

}

};

#endif

We’ll also need some functions to help us clean up IntArrays. First, we’ll write a destructor, which simply deallocates any dynamically allocated data. Second, we’ll write a function called erase(), which will erase the array and set the length to 0.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

~IntArray()

{

delete[]m_data;

// we don't need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway

}

voiderase()

{

delete[]m_data;

// We need to make sure we set m_data to nullptr here, otherwise it will

// be left pointing at deallocated memory!

m_data=nullptr;

m_length=0;

}

Now let’s overload the [] operator so we can access the elements of the array. We should bounds check the index to make sure it’s valid, which is best done using the assert() function. We’ll also add an access function to return the length of the array. Here’s everything so far:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

#ifndef INTARRAY_H

#define INTARRAY_H

#include <cassert> // for assert()

classIntArray

{

private:

intm_length;

int*m_data;

public:

IntArray():

m_length(0),m_data(nullptr)

{

}

IntArray(intlength):

m_length(length)

{

assert(length>=0);

if(length>0)

m_data=newint[length];

else

m_data=nullptr;

}

~IntArray()

{

delete[]m_data;

// we don't need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway

}

voiderase()

{

delete[]m_data;

// We need to make sure we set m_data to nullptr here, otherwise it will

// be left pointing at deallocated memory!

m_data=nullptr;

m_length=0;

}

int&operator[](intindex)

{

assert(index>=0&&index<m_length);

returnm_data[index];

}

intgetLength(){returnm_length;}

};

#endif

At this point, we already have an IntArray class that we can use. We can allocate IntArrays of a given size, and we can use the [] operator to retrieve or change the value of the elements.

However, there are still a few thing we can’t do with our IntArray. We still can’t change its size, still can’t insert or delete elements, and we still can’t sort it.

First, let’s write some code that will allow us to resize an array. We are going to write two different functions to do this. The first function, Reallocate(), will destroy any existing elements in the array when it is resized, but it will be fast. The second function, Resize(), will keep any existing elements in the array when it is resized, but it will be slow.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

// reallocate resizes the array. Any existing elements will be destroyed. This function operates quickly.

voidreallocate(intnewLength)

{

// First we delete any existing elements

erase();

// If our array is going to be empty now, return here

if(newLength<=0)

return;

// Then we have to allocate new elements

m_data=newint[newLength];

m_length=newLength;

}

// resize resizes the array. Any existing elements will be kept. This function operates slowly.

voidresize(intnewLength)

{

// if the array is already the right length, we're done

if(newLength==m_length)

return;

// If we are resizing to an empty array, do that and return

if(newLength<=0)

{

erase();

return;

}

// Now we can assume newLength is at least 1 element. This algorithm

// works as follows: First we are going to allocate a new array. Then we

// are going to copy elements from the existing array to the new array.

// Once that is done, we can destroy the old array, and make m_data

// point to the new array.

// First we have to allocate a new array

int*data=newint[newLength];

// Then we have to figure out how many elements to copy from the existing

// array to the new array. We want to copy as many elements as there are

// in the smaller of the two arrays.

if(m_length>0)

{

intelementsToCopy=(newLength>m_length)?m_length:newLength;

// Now copy the elements one by one

for(intindex=0;index<elementsToCopy;++index)

data[index]=m_data[index];

}

// Now we can delete the old array because we don't need it any more

delete[]m_data;

// And use the new array instead! Note that this simply makes m_data point

// to the same address as the new array we dynamically allocated. Because

// data was dynamically allocated, it won't be destroyed when it goes out of scope.

m_data=data;

m_length=newLength;

}

Whew! That was a little tricky!

Many array container classes would stop here. However, just in case you want to see how insert and delete functionality would be implemented we’ll go ahead and write those too. Both of these algorithms are very similar to resize().

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

voidinsertBefore(intvalue,intindex)

{

// Sanity check our index value

assert(index>=0&&index<=m_length);

// First create a new array one element larger than the old array

int*data=newint[m_length+1];

// Copy all of the elements up to the index

for(intbefore=0;before<index;++before)

data[before]=m_data[before];

// Insert our new element into the new array

data[index]=value;

// Copy all of the values after the inserted element

for(intafter=index;after<m_length;++after)

data[after+1]=m_data[after];

// Finally, delete the old array, and use the new array instead

delete[]m_data;

m_data=data;

++m_length;

}

voidremove(intindex)

{

// Sanity check our index value

assert(index>=0&&index<m_length);

// If this is the last element in the array, set the array to empty and bail out

if(m_length==1)

{

erase();

return;

}

// First create a new array one element smaller than the old array

int*data=newint[m_length-1];

// Copy all of the elements up to the index

for(intbefore=0;before<index;++before)

data[before]=m_data[before];

// Copy all of the values after the removed element

for(intafter=index+1;after<m_length;++after)

data[after-1]=m_data[after];

// Finally, delete the old array, and use the new array instead

delete[]m_data;

m_data=data;

--m_length;

}

// A couple of additional functions just for convenience

voidinsertAtBeginning(intvalue){insertBefore(value,0);}

voidinsertAtEnd(intvalue){insertBefore(value,m_length);}

Here is our IntArray container class in its entirety.

IntArray.h:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

#ifndef INTARRAY_H

#define INTARRAY_H

#include <cassert> // for assert()

classIntArray

{

private:

intm_length;

int*m_data;

public:

IntArray():

m_length(0),m_data(nullptr)

{

}

IntArray(intlength):

m_length(length)

{

assert(length>=0);

if(length>0)

m_data=newint[length];

else

m_data=nullptr;

}

~IntArray()

{

delete[]m_data;

// we don't need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway

}

voiderase()

{

delete[]m_data;

// We need to make sure we set m_data to nullptr here, otherwise it will

// be left pointing at deallocated memory!

m_data=nullptr;

m_length=0;

}

int&operator[](intindex)

{

assert(index>=0&&index<m_length);

returnm_data[index];

}

// reallocate resizes the array. Any existing elements will be destroyed. This function operates quickly.

voidreallocate(intnewLength)

{

// First we delete any existing elements

erase();

// If our array is going to be empty now, return here

if(newLength<=0)

return;

// Then we have to allocate new elements

m_data=newint[newLength];

m_length=newLength;

}

// resize resizes the array. Any existing elements will be kept. This function operates slowly.

voidresize(intnewLength)

{

// if the array is already the right length, we're done

if(newLength==m_length)

return;

// If we are resizing to an empty array, do that and return

if(newLength<=0)

{

erase();

return;

}

// Now we can assume newLength is at least 1 element. This algorithm

// works as follows: First we are going to allocate a new array. Then we

// are going to copy elements from the existing array to the new array.

// Once that is done, we can destroy the old array, and make m_data

// point to the new array.

// First we have to allocate a new array

int*data=newint[newLength];

// Then we have to figure out how many elements to copy from the existing

// array to the new array. We want to copy as many elements as there are

// in the smaller of the two arrays.

if(m_length>0)

{

intelementsToCopy=(newLength>m_length)?m_length:newLength;

// Now copy the elements one by one

for(intindex=0;index<elementsToCopy;++index)

data[index]=m_data[index];

}

// Now we can delete the old array because we don't need it any more

delete[]m_data;

// And use the new array instead! Note that this simply makes m_data point

// to the same address as the new array we dynamically allocated. Because

// data was dynamically allocated, it won't be destroyed when it goes out of scope.

m_data=data;

m_length=newLength;

}

voidinsertBefore(intvalue,intindex)

{

// Sanity check our index value

assert(index>=0&&index<=m_length);

// First create a new array one element larger than the old array

int*data=newint[m_length+1];

// Copy all of the elements up to the index

for(intbefore=0;before<index;++before)

data[before]=m_data[before];

// Insert our new element into the new array

data[index]=value;

// Copy all of the values after the inserted element

for(intafter=index;after<m_length;++after)

data[after+1]=m_data[after];

// Finally, delete the old array, and use the new array instead

delete[]m_data;

m_data=data;

++m_length;

}

voidremove(intindex)

{

// Sanity check our index value

assert(index>=0&&index<m_length);

// If we're removing the last element in the array, we can just erase the array and return early

if(m_length==1)

{

erase();

return;

}

// First create a new array one element smaller than the old array

int*data=newint[m_length-1];

// Copy all of the elements up to the index

for(intbefore=0;before<index;++before)

data[before]=m_data[before];

// Copy all of the values after the removed element

for(intafter=index+1;after<m_length;++after)

data[after-1]=m_data[after];

// Finally, delete the old array, and use the new array instead

delete[]m_data;

m_data=data;

--m_length;

}

// A couple of additional functions just for convenience

voidinsertAtBeginning(intvalue){insertBefore(value,0);}

voidinsertAtEnd(intvalue){insertBefore(value,m_length);}

intgetLength(){returnm_length;}

};

#endif

Now, let’s test it just to prove it works:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

#include <iostream>

#include "IntArray.h"

intmain()

{

// Declare an array with 10 elements

IntArray array(10);

// Fill the array with numbers 1 through 10

for(inti=0;i<10;i++)

array[i]=i+1;

// Resize the array to 8 elements

array.resize(8);

// Insert the number 20 before element with index 5

array.insertBefore(20,5);

// Remove the element with index 3

array.remove(3);

// Add 30 and 40 to the end and beginning

array.insertAtEnd(30);

array.insertAtBeginning(40);

// Print out all the numbers

for(intj=0;j<array.getLength();j++)

std::cout<<array[j]<<" ";

return0;

}

This produces the result:

40 1 2 3 5 20 6 7 8 30

Although writing container classes can be pretty complex, the good news is that you only have to write them once. Once the container class is working, you can use and reuse it as often as you like without any additional programming effort required.

It is also worth explicitly mentioning that even though our sample IntArray container class holds a built-in data type (int), we could have just as easily used a user-defined type (e.g. a Point class).

One more thing: If a class in the standard library meets your needs, use that instead of creating your own. For example, instead of using IntArray, you’re better off using std::vector<int>. It’s battle tested, efficient, and plays nicely with the other classes in the standard library. But this won’t always be possible, so it’s good to know how to create your own when you need to. We’ll talk more about containers in the standard library once we’ve covered a few more fundamental topics.

121 comments to 10.6 — Container classes

in your reallocate(int newLength) function you are checking newLength. If it is smaller you just create an empty array. Wouldn't it be better to make such a check in the constructor IntArray(int length), too? Either by an assert or like

1

2

3

4

5

6

7

8

9

10

11

12

IntArray(intlength):

m_length(length)

{

// I would have liked to delegate to IntArray(), but conditional delegation is not possible, right?

if(length<=0)

{

m_length=0;

m_data=nullptr;

}

else

m_data=newint[length];

}

Also I was thinking that it is dangerous not to assign values (zeros) to the array upon construction or when resizing with newLength > m_length to the elements with index >= m_length. Otherwise you will have garbage values in the array. If the user of IntArray tries to access elements of his container before assigning, he might get unexpected results. Well, it might also be unexpected, if he encounters zeros, but at least it is a bit more deterministic.

While writing this question I encountered something funny about not initialized pointers. I am going to ask that the lesson about "new" and "delete".

Yes, it would be better to assert or throw an exception in the case where the user passes in an invalid input. I've updated the example to either assert (if length is negative) or provide deterministic values (if length is 0).

> I think what confuses most beginners (including myself) is a & b are both dynamically allocated.

No! The memory whose address is assigned to a is dynamically allocated. Pointers a and b are statically allocated on the stack. When function doSomething() ends, pointer a is destroyed, and the address of the dynamically allocated memory is returned and assigned to b. There's no leak here, simply a transfer between two pointers. If b was not deleted in main(), then the memory would be leaked because once b is destroyed, there's no other pointers to the dynamically allocated memory.

1) Keep in mind what this function is doing: Allocating a new array to replace the old one (which gets deleted). If we deleted the new array too, then the object wouldn't have an array to hold data.

No memory leaks here either. Later on in the function, we set m_data = data, so m_data now points at the newly allocated memory, and because m_data is a member variable, it isn't destroyed or lost at the end of the function. When the class object is destroyed, the destructor will delete[] m_data, and the array will be deallocated then.

Hello Alex,
I have a question. In 8.9 — Class code and header files's topic you quoted " we can put function declarations inside header files in order to use those functions in multiple files or even multiple projects. Classes are no different. Class definitions can be put in header files in order to facilitate reuse in multiple files or multiple projects".

My question: Is this what you quoted above related to IntArray class.
I mean, did you the create header file for IntArray class to use by other files or by other projects as well? i.e. Not only one file or project can be used container IntArray class and others as well, So the purpose of putting IntArray class in header file helps to reuse by other file or projects.Am I right?
If i am not right here- could you explain? what is the purpose of putting the IntArray class in header file and main() for .cpp file separately?

I know that in the 1st case, they both would have same memory addresses and in the 2nd case, they would have diff. memory addresses.
But still, the segment I asked you to replace doesn't work ! Help Plz !
Thanks in Advance 🙂

Oh God ! Why am I so confused?
Sorry Alex !
The segment that I asked you to replace actually worked perfectly !
So, Now My Only Question is,
You haven’t deleted data pointer anywhere in the INT ARRAY class, so won’t there be memory leaks !?
Thanks 😉

Sorry, my bad! But isn't m_data pointing at data ( m_data = data;) so they are both connected. Well im learning so im not the right person to give answer but what i understand is that m_data is later on assigned to point at same. so deleting m_data is same as delete data? so if we do several remove they keep stacking with point to point or maybe im just wrong but i think you got a point
Well its better to wait for Alex answer 🙂

No. We're doing 4 things in this code:
* Dynamically allocating a new array (held by pointer data)
* Copying the old array into the new array
* Deleting the old array (held by m_data)
* Setting m_data to point to the new array (m_data = data)

We don't have a memory leak here because of this line:

1

m_data=data;

This sets m_data to point at the new array we created. If we deleted data, then m_data would be a dangling pointer, and our class would have unexpected behavior the next time we dereferenced m_data.

Exactly What I thought !
data pointer is the extra ptr we created....
I am deleting it explicitly (even though there is no need to), I just meant to ask That we can perform deep copying too there ! 🙂
Thanks For The Help !!

Hi!
When i run the code i get error do uk why? i copy paste the code and it gives me Assertion failed! index >= 0 && index < m_length.
I copy pasted the code and it doesnt work

By the way i have so much problem understanding when it comes to std::vector and std::vector when u use pointer to object in vector i get confused, any advice what knowledge i am missing? why use pointer to object and not just object?
Thanks in advice:)

I think i found the error! in ur insertBefore(int value, int index) when it calls insertAtEnd then u send insertBefore(int value, m_length) which will trigger the assert! so u need to have <= to be able to use the insertAtEnd to use that function!

Thanks for the respond! About that pointer to object there is something i am missing cause it doesnt ring a bell for me, and it always makes me unsure. Do u got any advice where i should go back and read cause it feels like something i am missing. like the 10.4 why pointer to patient and doctor in std::vector everything else i understand just something i always struggle with

Hmmm, I don't know if I've covered this topic anywhere explicitly. I updated lesson 10.4 to note that aggregations are normally implemented using pointers. In the doctor/patient example, we use Doctor and Patient pointers so that each can reference the other without implying ownership. If we tried to use normal members, it wouldn't work for many reasons. First, a Doctor doesn't have a Patient, so semantically the model is wrong. Second, if a Doctor included a Patient member and a Patient included a Doctor member, you'd get a circular reference that wouldn't compile. Third, a normal member confers ownership -- if a Doctor had a Patient member, when the Doctor was destroyed, the Patient would be too.

So, generally speaking, with arrays/vectors, you use normal members when you're modelling compositions, and pointers when you're modeling aggregations or associations.

" // If this is the last element in the erase, set the array to empty and bail out
if (m_length == 1)
{
erase();
return;
} "
I think this should be:
//If there is a single element in the array and erase() will render it empty do the following:
...

Regarding:
"We need to make sure we set m_data to 0 here, otherwise it will be left pointing at deallocated memory!"
Should this not be:
"We need to set m_data to *nullptr* here, otherwise it will be left pointing at deallocated memory!"
...
And further on:
m_data = nullptr; //instead of 0
m_length = 0;
...

thanks 🙂 can you say when destructor gets called in that code?
The constructor is called when the object is created and destructor is called when object is destroyed, but what is the code that says the object is destroyed?

It depends. Objects can get destroyed in a few ways:
* If they have automatic duration (e.g. non-static local variables), at the end of the block they were declared in
* If they have static duration, at the end of the program.
* If they are allocated dynamically, whenever the user manually destroys them using the delete keyword.

Hi Alex:
I am trying to separate your example IntArray.h to header and body according to section 8.9, but always got error.
Could it be possible for you, or anybody else, to correct my code?
Thanks a lot!
Yifang

The header:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

//Intarray.h

#ifndef INTARRAY_H

#define INTARRAY_H

classIntArray

{

private:

intm_length;

int*m_data;

public:

IntArray():m_length(0),m_data(nullptr){};

IntArray(intlength):m_length(length){m_data=newint[length];}

~IntArray(){delete[]m_data;}

// we don't need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway

voiderase(){}

int&operator[](intindex){returnm_data[index];}

voidreallocate(intnewLength){}

voidresize(intnewLength){}

voidinsertBefore(intvalue,intindex){}

voidremove(intindex){}

// A couple of additional functions just for convenience

voidinsertAtBeginning(intvalue){insertBefore(value,0);}

voidinsertAtEnd(intvalue){insertBefore(value,m_length);}

intgetLength(){returnm_length;}

};

#endif

And this is the body part:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

//Intarray.cpp

#include "IntArray.h"

#include <cassert> // for assert()

voidIntArray::erase()

{

delete[]m_data;

// We need to make sure we set m_data to 0 here, otherwise it will

// be left pointing at deallocated memory!

m_data=0;

m_length=0;

}

int&IntArray::operator[](intindex)

{

assert(index>=0&&index<m_length);

returnm_data[index];

}

// reallocate resizes the array. Any existing elements will be destroyed. This function operates quickly.

voidIntArray::reallocate(intnewLength)

{

// First we delete any existing elements

erase();

// If our array is going to be empty now, return here

if(newLength<=0)

return;

// Then we have to allocate new elements

m_data=newint[newLength];

m_length=newLength;

}

// resize resizes the array. Any existing elements will be kept. This function operates slowly.

voidIntArray::resize(intnewLength)

{

// if the array is already the right length, we're done

if(newLength==m_length)

return;

// If we are resizing to an empty array, do that and return

if(newLength<=0)

{

erase();

return;

}

// Now we can assume newLength is at least 1 element. This algorithm

// works as follows: First we are going to allocate a new array. Then we

// are going to copy elements from the existing array to the new array.

// Once that is done, we can destroy the old array, and make m_data

// point to the new array.

// First we have to allocate a new array

int*data=newint[newLength];

// Then we have to figure out how many elements to copy from the existing

// array to the new array. We want to copy as many elements as there are

// in the smaller of the two arrays.

if(m_length>0)

{

intelementsToCopy=(newLength>m_length)?m_length:newLength;

// Now copy the elements one by one

for(intindex=0;index<elementsToCopy;++index)

data[index]=m_data[index];

}

// Now we can delete the old array because we don't need it any more

delete[]m_data;

// And use the new array instead! Note that this simply makes m_data point

// to the same address as the new array we dynamically allocated. Because

// data was dynamically allocated, it won't be destroyed when it goes out of scope.

m_data=data;

m_length=newLength;

}

voidIntArray::insertBefore(intvalue,intindex)

{

// Sanity check our index value

assert(index>=0&&index<=m_length);

// First create a new array one element larger than the old array

int*data=newint[m_length+1];

// Copy all of the elements up to the index

for(intbefore=0;before<index;++before)

data[before]=m_data[before];

// Insert our new element into the new array

data[index]=value;

// Copy all of the values after the inserted element

for(intafter=index;after<m_length;++after)

data[after+1]=m_data[after];

// Finally, delete the old array, and use the new array instead

delete[]m_data;

m_data=data;

++m_length;

}

voidIntArray::remove(intindex)

{

// Sanity check our index value

assert(index>=0&&index<m_length);

// If this is the last element in the erase, set the array to empty and bail out

Thanks a lot!
There are always too many options for any subject, which is the complexity of C++, so that similar but more concise tutorial is needed for beginners.
Very good tutorials and thank you again!