Test. An application of type ConsoleApplication. The template class CArray
Using this example, you can learn to develop your own template classes in which memory is allocated dynamically.
Contents
- Task
- Instructions
- 1. The text of the program
- 2. The result of the program
- 3. Explanation of the program
- 3.1. Calling functions for testing class work
- 3.2. Why is the copy constructor and copy operator are implemented in the class?
- 3.3. Features of memory allocation in the new operator
- 3.4. Freeing memory in a class with delete[] operator
- 3.5. Additional internal functions of the class
- 3.6. Features of generating random numbers. Functions srand(), time(), rand()
- Related topics
Search other websites:
Task
Develop a template class CArray – an array of data of an arbitrary type T and tests that demonstrate working with this class. Memory for the data array is allocated dynamically.
The following methods should be implemented:
- default constructor;
- copy constructor;
- destructor;
- method push_back (T _value) to add an _value element to the end of the array;
- method erase(int _index) which removes an item from the array at the given index;
- the method insert(int _index) which inserts an element into the array at the specified index;
- method size() returns the size of the array;
- method clear() that clears the array;
- operator function operator[](), which overloads the operator [ ] of indexing array elements and returns the value of the array item at the specified index;
- method print(), intended for displaying the contents of the array on the screen.
The solution should demonstrate the operation of the class using the following tests:
- Work with numbers (int)
- 1.1. Adding 20 random numbers in a cycle from 0 to 100.
- 1.2. Sort the resulting set of numbers in ascending order.
- 1.3. Removing every second element in the array.
- 1.4. Inserting 10 random numbers ranging from 0 to 100 at random positions.
- 1.5. Container cleaning.
- Work with objects (std::string)
- 2.1. Adding in a cycle of 15 randomly selected words consisting of lowercase Latin letters.
- 2.2. Sort the resulting set of words in ascending order.
- 2.3. Removing every word that includes any of the letters a, b, c, d, e.
- 2.4. Inserting three randomly selected words into random positions.
⇑
Instructions
1. The text of the program
An implementation of a program like Console Application has the following form.
// The task about the template class #include <iostream> #include <new> #include <vector> #include <time.h> using namespace std; template <typename T> class CArray { public: // default constructor CArray() { count = 0; } // copy constructor CArray(const CArray& _A) { try { // attempt to allocate memory for an array of type T A = new T[_A.count]; count = _A.count; for (int i = 0; i < count; i++) A[i] = _A.A[i]; } catch (bad_alloc e) { count = 0; cout << "Error. Cannot allocate memory." << endl; cout << e.what() << endl; } } // copy operator CArray& operator=(const CArray& _A) { // release previously allocated memory if (count > 0) delete[] A; // allocate memory try { A = new T[_A.count]; count = _A.count; for (int i = 0; i < count; i++) A[i] = _A.A[i]; } catch (bad_alloc e) { count = 0; cout << "Error. Cannot allocate memory." << endl; cout << e.what() << endl; } return *this; } // destructor ~CArray() { // release previously allocated memory if (count > 0) delete[] A; } // add item to the end of array void push_back(T value) { T* A2; // additional pointer to array // attempt to allocate memory try { A2 = new T[count + 1]; // if the memory is allocated, then increase by 1 the number of items count++; // copy array A to array A2 for (int i = 0; i < count - 1; i++) A2[i] = A[i]; // add item value A2[count - 1] = value; // release the memory allocated for the array A if ((count - 1) > 0) delete[] A; // redirect pointer A to array A2 A = A2; } catch (bad_alloc e) { cout << "Error. Cannot allocate memory." << endl; cout << e.what() << endl; } } // method that outputs array A - needed for testing void print() { cout << "Array A: " << endl; if (!count) { cout << "Array is empty." << endl; return; } for (int i = 0; i < count; i++) cout << A[i] << " "; cout << endl; } // add an item to the array at a given index void insert(int _index, const T& _value) { // validation of the index if (!CheckIndex(_index)) { cout << "Error. Bad index. "; return; } T* A2; A2 = Alloc(count + 1); // attempt to allocate memory if (A2 == nullptr) { cout << "Error. Method insert(). Cannot allocate memory." << endl; return; } // copy A to A2 before the _index position for (int i = 0; i < _index; i++) A2[i] = A[i]; // insertion at _index position A2[_index] = _value; // copy A to A2 after position _index for (int i = _index + 1; i < count + 1; i++) A2[i] = A[i - 1]; // release preallocated memory for array A if (count > 1) delete[] A; count++; // increase the number of array items by 1 // redirect pointer A to array A2 A = A2; } // method erase() - removes an item from the array at a given index void erase(int _index) { // checking if the index is correct if (!CheckIndex(_index)) { cout << "Bad index." << endl; return; } // the loop to delete from an array of an item in position _index for (int i = _index; i < count - 1; i++) A[i] = A[i + 1]; count--; // reduce the number of array items by 1 // reallocate memory T* A2 = Alloc(count); // copy A to A2 for (int i = 0; i < count; i++) A2[i] = A[i]; // release the memory allocated for the array A if (count > 0) delete[] A; // redirect pointer A to A2 A = A2; } // method returning array size int size() { return count; } // array clearing method void clear() { if (count > 0) { delete[] A; count = 0; } } // operator function operator[], overloads the array indexing operator T& operator[](int _index) { if (CheckIndex(_index)) return A[_index]; else { T value = 0; cout << "Bad index." << endl; return value; } } protected: T* A; // data array int count; // the number of items in the array // internal method that checks if index value is within acceptable limits // it returns true if the index is correct bool CheckIndex(int _index) { if ((_index < 0) || (_index >= count)) return false; return true; } // a method that allocates memory for an array of type T, // method returns a pointer to the allocated array T* Alloc(int _count) { T* A2 = nullptr; try { A2 = new T[_count]; } catch (bad_alloc e) { cout << e.what() << endl; return nullptr; } return A2; // return the pointer to the selected fragment } }; // Tests to demonstrate class work // 1.1. Add 20 random numbers in the loop from 0 to 100 void Test_1_1(CArray<int>& A) { int i, number; cout << "Test 1.1. Form the array with 20 random numbers." << endl; srand(time(NULL)); for (i = 0;i < 20;i++) { // generate a random number from 0 to 100 number = rand() % 100; // add random number to the end of the array A.push_back(number); } } // 1.2. Sorting a set of numbers in ascending order void Test_1_2(CArray<int>& A) { int i, j; int t; cout << "Test 1.2. Sorting the array." << endl; // insertion sorting for (i=0;i<A.size()-1;i++) for (j=i; j>=0; j--) if (A[j] > A[j + 1]) { t = A[j]; A[j] = A[j + 1]; A[j + 1] = t; } } // 1.3. Delete every second item void Test_1_3(CArray<int>& A) { CArray<int> B; // a new array cout << "Test 1.3. Deleting every secont item." << endl; B.clear(); for (int i = 0; i < A.size();i++) { if (i % 2 == 1) B.push_back(A[i]); } A = B; } // 1.4. Insertion 10 random numbers ranging from 0 to 100 at random positions void Test_1_4(CArray<int>& A) { int i; int number, pos; int t; cout << "Test 1.4. Inserting 10 random numbers to random positions" << endl; // snap to the current time when generating random numbers srand(time(NULL)); t = 0; // variable that determines the number of added numbers for (i = 0; i < 10; i++) { number = rand() % 100; // random number pos = rand() % (10 + t); // random position (0..10+t) A.insert(pos, number); t++; // number of added numbers } } // 1.5. Container cleaning void Test_1_5(CArray<int>& A) { cout << "Test 1.5. Clear the array." << endl; A.clear(); } // Demonstration of the class work with int numbers void DemoInt() { CArray<int> A; // array to be tested // Test 1.1 Test_1_1(A); A.print(); // display array to the screen // Test 1.2 Test_1_2(A); A.print(); // Test 1.3 Test_1_3(A); A.print(); // Test 1.4 Test_1_4(A); A.print(); // Test 1.5 Test_1_5(A); A.print(); } // 2. Work with objects (std::string) // 2.1. Adding 15 randomly selected words consisting of Latin letters in lowercase in a loop void Test_2_1(CArray<string>& A) { int i, j; int len; // the length of the word string word; char symbol; cout << "Test 2.1. Adding 15 new words." << endl; srand(time(NULL)); // word formation loop for (i = 0; i < 15; i++) { len = rand() % 6 + 1; // random word length from 1 to 6 // generate a random word word = ""; for (j = 0;j < len;j++) { // get a random symbol symbol = 'a' + rand() % ((int)'z' - (int)'a' + 1); word += symbol; // add symbol to the word } A.push_back(word); // add a random word to array } } // 2.2. Sort words in ascending order void Test_2_2(CArray<string>& A) { int i, j; string t; cout << "Test 2.2. Sorting words." << endl; // insertion sorting for (i = 0;i < A.size() - 1;i++) { for (j=i; j>=0; j--) if (A[j] > A[j + 1]) { t = A[j]; A[j] = A[j + 1]; A[j + 1] = t; } } } // 2.3. Удаление каждого слова, включающего в себя любую из букв a,b,c,d,e void Test_2_3(CArray<string>& A) { // additional variables int i, j; string word; CArray<string> B; bool f_delete; cout << "Test 2.3. Deleting words wit characters a..z " << endl; // loop to create a new array B that does not contain words for (i = 0; i < A.size(); i++) { word = A[i]; f_delete = false; for (j=0; j<word.length(); j++) if (('a' <= word[j]) && (word[j] <= 'e')) // if the symbol is within 'a' .. 'e' { f_delete = true; // it can be deleted break; } // add a word to array B that does not contain 'a' .. 'e' if (!f_delete) B.push_back(word); } A = B; // copy array B to A } // 2.4. Insert 3 new random words into random positions void Test_2_4(CArray<string>& A) { int i, j; int len; // random length of random word string word; // random word char symbol; // random symbol int position; // random position (0, 1, ...) int t; // number of added words cout << "Test 2.4. Inserting 3 new random words to random positions" << endl; // snap to the timer when generating random words srand(time(NULL)); // The loop of adding a random strings for (i = 0, t = 0; i < 3; i++, t++) { // get a random position position = rand() % (A.size() + t); // get a random word length of 1..6 len = rand() % 6 + 1; // generate a random word word = ""; for (j = 0; j < len; j++) { // form a random symbol symbol = 'a' + rand() % ((int)'z' - (int)'a' + 1); word += symbol; // add symbol to the word } // insert a random word into a random position A.insert(position, word); } } // A method that demonstrates how to work with std::string objects void DemoString() { CArray<string> A; // declare an array of words for testing Test_2_1(A); A.print(); Test_2_2(A); A.print(); Test_2_3(A); A.print(); Test_2_4(A); A.print(); } int main() { DemoInt(); DemoString(); }
⇑
2. The result of the program
Test 1.1. Form the array with 20 random numbers. Array A: 40 55 83 90 71 0 38 98 15 49 44 69 74 82 84 90 88 14 93 10 Test 1.2. Sorting the array. Array A: 0 10 14 15 38 40 44 49 55 69 71 74 82 83 84 88 90 90 93 98 Test 1.3. Deleting every secont item. Array A: 10 15 40 49 69 74 83 88 90 98 Test 1.4. Inserting 10 random numbers to random positions Array A: 10 15 38 93 84 40 83 71 49 69 40 88 74 74 83 15 88 90 44 98 Test 1.5. Clear the array. Array A: Array is empty. Test 2.1. Adding 15 new words. Array A: txkhs crt paqkm shcni wgagcv ye jlww rayr qtjc usd bprcx s yvd pwxb jobx Test 2.2. Sorting words. Array A: bprcx crt jlww jobx paqkm pwxb qtjc rayr s shcni txkhs usd wgagcv ye yvd Test 2.3. Deleting words wit characters a..z Array A: jlww s txkhs Test 2.4. Inserting 3 new random words to random positions Array A: jlww acrtk xk s qkm txkhs
⇑
3. Explanation of the program
3.1. Calling functions for testing class work
From the main() function, the DemoInt() and DemoString() functions are called. Each of the functions calls the corresponding test.
The DemoInt() function calls tests for the int type. Each test is designed as a separate function: Test_1_1(), Test_1_2(), Test_1_3(), Test_1_4(), Test_1_5(). Each of the tested functions receives as input parameter a reference to an array of type int.
CArray<int>& A
Since the passing of the array occurs by reference, that is, the ability to change the input array in the function.
The DemoString() function invokes the tests for the std::string class. The class string is implemented in the std namespace and is designed to work with char* character strings. The class contains a number of methods for working with character strings.
Each test for the string type is designed as a separate function: Test_2_1(), Test_2_2(), Test_2_3(), Test_2_4().As in the case of the int type, any of the functions receives as input parameter a reference to an array of the string type
CArray<string>& A
In each function tested, the input array is changed (passing the array by reference).
⇑
3.2. Why is the copy constructor and copy operator are implemented in the class?
The CArray class declares a dynamic array A of generalized type T. In fact, a pointer A to type T is declared
T* A; // data array
Depending on the situation, for array A, memory is allocated dynamically using the new operator. If memory is allocated dynamically in a class, then this class must have an implementation of the copy constructor and copy operator in order to avoid the disadvantages of bitwise copying. More details about the need for declarations in the class of the copy constructor and copy operator are described in the topic:
⇑
3.3. Features of memory allocation in the new operator
In a class, in different functions, memory is allocated using the new operator. There is a risk that memory may not be allocated. In this case, the system throws an exception. To ensure that the program does not stop, but displays the corresponding message, the call to the new operator is taken in a try…catch block as shown below
... try { // attempt to allocate memory for array of type T A = new T[_A.count]; count = _A.count; for (int i = 0; i < count; i++) A[i] = _A.A[i]; } catch (bad_alloc e) { count = 0; cout << "Error. Cannot allocate memory." << endl; cout << e.what() << endl; } ...
As you can see from the code, an exception of type bad_alloc is caught in the catch block. bad_alloc is a class that describes an exception when memory is not allocated by the new operator. The bad_alloc class is inherited from the exception class and is part of the hierarchy of classes that describe exceptions in C ++.
⇑
3.4. Freeing memory in a class with delete[] operator
Since, memory for array A is allocated as for an array (and not a single element)
A = new T[count]
then you need to release it by the operator
delete[] A;
If memory was allocated as for a single element, then delete was to be used instead of delete[].
⇑
3.5. Additional internal functions of the class
In the class, in the private section, the following additional internal functions are implemented
- CheckIndex() – checks if the index of array A is within acceptable boundaries;
- Alloc() – returns a pointer to the allocated memory area of a given _count size, which is an input parameter.
These functions are called several times in different methods of the class.
⇑
3.6. Features of generating random numbers. Functions srand(), time(), rand()
In order to generate a random number in the program, the following functions are used:
- srand() – sets the starting number for the rand() function. Based on this number, a sequence of random numbers will be generated by the rand() function;
- rand() – returns an integer random number (generates a random number);
- time() – a function from the time.h library.
Function time() with NULL parameter
time(NULL)
returns the number of milliseconds that have passed since January 1, 1970. This function is necessary in order to form a starting number for a random number generator. The number of milliseconds will always be different, since the launch of the program depends on the user and it is impossible to foresee the moment of launch.
The function time() is nested as a parameter in the srand() function
srand(time(NULL));
The srand() function sets the starting number, which will be used as the basis for generating a sequence of random numbers with the rand() function. This means that the union of the srand(), time(), rand() functions allows to generate different sequences of numbers with each call.
⇑
Related topics
⇑