Developing a class that implements an array of char* strings
In C++, a string can be represented as either the string type or the char* type. The string type is a class and has a powerful arsenal of handy facilities for handling strings. Working with char* strings is often tricky.
This topic demonstrates the ArrayPChar class, which represents an array of char* strings. Working with strings of type char* is no less interesting than working with strings of type string. The topic will be useful for beginners in the study of issues of dynamic allocation/freeing of memory for an array, access by a pointer in an array of pointers, working with char* strings.
Contents
- Task
- Solution
- 1. General construction of the class. Adding internal variables
- 2. Development of additional class methods
- 2.1. Method CheckIndex(). Checking the correctness of an array index
- 2.2. Method CopyStr(char** dest, const char* source). Copy string with memory allocation
- 2.3. Method CopyArrayStr(char***, char**, int). Copying char* arrays with memory allocation
- 2.4. Method Free(char**, int). Freeing memory in an array
- 3. Development of basic methods for operating with strings in accordance with the condition of the problem
- 3.1. Parameterless constructor ArrayPChar()
- 3.2. Constructor with two parameters ArrayPChar(char**, int)
- 3.3. Copy constructor ArrayPChar(const ArrayPChar&)
- 3.4. Method GetAi(int). Reading a single line in the array
- 3.5. Method SetAi(const char* , int). Writing a string to an array at a specified index
- 3.6. Method Add(). Add string to the end of array
- 3.7. Method Del(). Removing an element at a given position
- 3.8. Operator method operator=(). Overloading the assignment operator
- 3.9. Destructor
- 3.10. Method Print(). Displaying an array to the screen
- 4. Method main(). Testing
- 5. The text of the entire program. Abridged version
- Related topics
Search other websites:
Task
Develop a class that operates on strings of type char*. The class must implement:
- internal variables that represent an array of strings;
- constructors that initialize an array in various ways;
- copy constructor;
- the GetAi() and SetAi() methods for accessing a separate line;
- operator function operator=(), which overrides the assignment operation. The function should implement assignment of arrays of strings;
- the Add() method for adding a string to the end of the array;
- Del() method for deleting a line from a given position;
- destructor.
⇑
Solution
1. General construction of the class. Adding internal variables
First of all, a class named ArrayPChar is declared and the following internal variables are declared in it:
- A – array of strings of type char*. Array type char**;
- length – the number of strings in array A.
#include <iostream> using namespace std; // Array of strings of type char* class ArrayPChar { private: // Internal data char** A; // array of strings int length; // number of strings in array A }
In the future, different methods will be added to this class in accordance with the condition of the problem.
⇑
2. Development of additional class methods
In the private section of the class, you need to introduce a number of general-purpose methods. These methods (functions) will be called from other methods. General purpose methods implement typical string and array operations.
2.1. Method CheckIndex(). Checking the correctness of an array index
The CheckIndex() method checks if the index of the string in the array is within acceptable limits. The method code is as follows:
// A method that determines if index is a valid array index. // The method returns true if the index value is correct. bool CheckIndex(int index) { return ((index >= 0) && (index < length)); }
This method needs to be copied to the private section of the class.
⇑
2.2. Method CopyStr(char** dest, const char* source). Copy string with memory allocation
Since copying strings is the main operation in this class, the implementation of the CopyStr() function, which copies one string to another, will be very useful. The peculiarity of this function is that inside the function memory is allocated for the destination string.
The function receives 2 parameters:
- original string source. String type char*;
- pointer to destination string dest (pointer type char**). Accessing the destination string *dest. The string *dest type is char*. The size of the *dest string is determined dynamically based on the size of the source string.
The function code is as follows:
// Internal function - copies the string source to dest, // memory for dest is allocated inside the function void CopyStr(char** dest, const char* source) { int i; // Allocate memory for *dest try { *dest = new char[strlen(source) + 1]; } catch (bad_alloc e) { cout << e.what() << endl; return; } // Copy with '\0' character inclusive for (i = 0; source[i]!='\0'; i++) (*dest)[i] = source[i]; (*dest)[i] = '\0'; }
The function text should be placed in the private section of the ArrayPChar class.
⇑
2.3. Method CopyArrayStr(char***, char**, int). Copying char* arrays with memory allocation
Based on the previous function for copying a string CopyStr(), the function for copying arrays CopyArrayStr() has been developed. CopyArrayStr() function receives 3 parameters:
- pointer to the destination array dest (pointer type char***). Access to array *dest (array type char**). Accessing the i-th line (*dest)[i]. Each line (*dest)[i] is of type char*;
- array source (array type – char**). The type of the string in the array – char*;
- the number of strings length in the source array.
Inside the CopyArrayStr() function, memory is allocated for the destination array *dest. The size of the allocated memory is based on the size of the source array.
The text of the function is as follows:
// A function that copies the array of strings source to the array dest. // Memory for the dest array is allocated inside the function void CopyArrayStr(char*** dest, char** source, int length) { int i, j; int n; // Allocate memory for array dest as an array of pointers try { *dest = new char* [length]; } catch (bad_alloc e) { cout << e.what() << endl; return; } // Loop copying strings for (i = 0; i < length; i++) CopyStr(&((*dest)[i]), source[i]); }
As you can see from the above code, to copy lines and allocate memory for these lines, the CopyArrayStr() function calls the CopyStr() function from section 2.2. To pass a pointer to a string (*dest)[i] to the CopyStr() function, use the following call
... CopyStr(&((*dest)[i]), source[i]); ...
The CopyArrayStr() function can be placed in the private section of the class.
⇑
2.4. Method Free(char**, int). Freeing memory in an array
A frequent operation is freeing memory for the internal array A. To implement this operation, the Free() function has been developed in the class, which frees memory for the input array. The function receives two parameters:
- array of strings;
- the number of strings length in the array.
The function text is as follows:
// Internal function that frees memory for the input array void Free(char** A, int length) { if (length > 0) { // Release the memory allocated for each string for (int i = 0; i < length; i++) delete[] A[i]; // Release memory for array of pointers to strings delete[] A; } }
Checking whether memory has been allocated is based on the length parameter. Memory is freed in two stages. First, the memory allocated for each line (element type char*) is freed. Then the memory allocated for the array of pointers as a whole is freed (array type char**, pointer type char*).
⇑
3. Development of basic methods for operating with strings in accordance with the condition of the problem
The public section of the class declares the methods that are required in accordance with the condition of the task.
3.1. Parameterless constructor ArrayPChar()
The parameterless constructor is called if an object of the ArrayPChar class is created in which there are no elements in the array A. For example,
// an instance (object) named AC is declared ArrayPChar AC; // a constructor without parameters is called
The constructor text without parameters is as follows
... public: // Constructor without parameters ArrayPChar() { A = nullptr; length = 0; } ...
⇑
3.2. Constructor with two parameters ArrayPChar(char**, int)
The constructor with two parameters is intended to initialize the current array with elements of another array of char** type. The constructor takes two parameters:
- array of strings _A. The type of array char**. The type of strings in array char*;
- number of strings in array – length.
... // Constructor that initializes internal data with the value of another array ArrayPChar(char** _A, int _length) { // Declare a local variables int i, j; // Copy A = _A CopyArrayStr(&A, _A, _length); // Set the number of strings length = _length; } ...
As you can see from the above code, all the work on copying arrays is performed by the CopyArrayStr() function, which is described in section 2.3.
⇑
3.3. Copy constructor ArrayPChar(const ArrayPChar&)
In this class, it is necessary to implement the copy constructor, since the class uses pointers for which memory is dynamically allocated. More details about the need to use the copy constructor are described in the topic:
The copy constructor text is as follows
// Copy constructor ArrayPChar(const ArrayPChar& _A) { CopyArrayStr(&A, _A.A, _A.length); length = _A.length; }
⇑
3.4. Method GetAi(int). Reading a single line in the array
Mandatory methods are methods that read/write a specific line located at position i. The GetAi() method returns a pointer to the array string at position i.
// Reading a string at index i char* GetAi(int i) { if (CheckIndex(i)) return A[i]; else return nullptr; }
⇑
3.5. Method SetAi(const char* , int). Writing a string to an array at a specified index
The SetAi() method implements writing a string of type char* to an array at a given position. The memory allocated for the previous string is reallocated to fit the size of the new string.
// Writing a string of type char* to string A[i] at index i, // The function returns true if the write was successful. bool SetAi(const char* str, int i) { // Checking if the index is within acceptable limits if (CheckIndex(i)) { // Release the pre-allocated memory for string A[i] if (A[i] != nullptr) delete[] A[i]; // Copy string: A[i] = str CopyStr(&A[i], str); return true; } return false; }
⇑
3.6. Method Add(). Add string to the end of array
The Add() method adds a string to the end of the array. The method declares an additional array variable A2 of type char**, which serves as a copy of the main array with an additional last element.
The general algorithm of the method is as follows:
- form a new array A2 with an additional last element;
- copy data from the main array A to the additional array A2;
- free the memory previously allocated for array A;
- redirect pointer from array A to the memory area allocated for array A2.
The method text is as follows
// Add string to the end of array void Add(const char* str) { // 1. Declare additional internal variables char** A2 = nullptr; // Additional local array // 2. Allocate memory for array A2 - 1 more item try { A2 = new char* [length + 1]; } catch (bad_alloc e) { cout << e.what() << endl; return; } // 3. Copy strings from A to A2 for (int i = 0; i < length; i++) CopyStr(&A2[i], A[i]); // 4. Free memory previously allocated for array A Free(A, length); // 5. Write last item to array A2, // the memory for A2[length] is allocated inside the function CopyStr() CopyStr(&A2[length], str); // 6. Increase the number of strings by 1 length++; // 7. Redirect pointer A to A2 A = A2; }
⇑
3.7. Method Del(). Removing an element at a given position
The operation of removing an element from a given position is useful for an array. The Del() method is used to implement this operation. The method uses an additional array variable A2, which serves as a copy of the main array A without the row to be deleted. The position of the string is determined by the index parameter.
The general algorithm of the method is as follows:
- copy elements from array A to array A2 before the position index. The element at position index is not included in the resulting array A2;
- copy elements from array A to array A2 after position index. Thus, the element at position index is not included in the resulting array A2;
- redirect pointer from array A to array A2.
The method text is as follows
// Removing an element from a given position index = 0, 1, ..., length-1 void Del(int index) { // Additional array char** A2 = nullptr; // Checking if the index value is correct if (CheckIndex(index)) { // Allocate memory for array A2 - 1 line less try { A2 = new char* [length - 1]; } catch (bad_alloc e) { cout << e.what() << endl; return; } // Cycle of copying data from A to A2 // Before index position for (int i = 0; i < index; i++) CopyStr(&A2[i], A[i]); // Copy data A2[i] = A[i] // After index position for (int i = index + 1; i < length; i++) CopyStr(&A2[i - 1], A[i]); // Free memory allocated for A Free(A, length); // Decrease the total number of strings by 1 length--; // Redirect pointers A = A2; } }
⇑
3.8. Operator method operator=(). Overloading the assignment operator
The operator method (function) operator=() is required for the following assignment to be performed correctly
A1 = A2;
where A1, A2 – instances (objects) of type ArrayPChar.
If you do not overload the assignment operator(=), then the bitwise copying provided by the compiler by default will be performed. If pointers are used in a class (as in our ArrayPChar class), bitwise copying of pointers leads to the fact that pointers of different instances (objects) of the class point to the same memory location, which is not allowed. After the destruction of class objects, the destructor is called, which frees the same memory area two times (for each object separately). The first time (for the first object) destruction will occur correctly, the second time (for the second object) the system will throw an exception, since the memory has already been freed. As a result, the program will “crash”. More details about the disadvantages of bitwise copying and the need to overload the assignment operator and copy constructor are described here.
The text of the operator method operator=() is as follows
// Overloaded assignment operator ArrayPChar& operator=(ArrayPChar& _A) { // 1. Declare additional internal variables char** A2; // 2. Free memory previously allocated for array A Free(A, length); // 3. Check for the correct values if (_A.length <= 0) { length = 0; A = nullptr; return *this; } // 4. Assignment A = _A.A CopyArrayStr(&A, _A.A, _A.length); // 5. Install the new number of strings length = _A.length; // 6. Return the current instance return *this; }
⇑
3.9. Destructor
The destructor is designed to free the dynamically allocated memory after the ArrayPChar object is destroyed. In our case, the memory allocated dynamically is freed in the internal Free() method. Accordingly, the program code of the destructor is as follows
// Destructor ~ArrayPChar() { // Free memory allocated for array A Free(A, length); }
⇑
3.10. Method Print(). Displaying an array to the screen
When developing a class, it is always advisable to implement a method that outputs the data content of an instance of the class. This is necessary for control, testing, etc. In our case, the Print() method is implemented, the text of which is as follows
// Method for outputting an array of strings to the screen, // the method receives a comment as a parameter, which is also displayed. void Print(const char* text) { cout << "----------------" << endl; cout << text << endl; if (length <= 0) { cout << "Array is empty." << endl; return; } // Print strings for (int i = 0; i < length; i++) { cout << A[i] << endl; } }
⇑
4. Method main(). Testing
The main() method demonstrates the use of the ArrayPChar class. Optionally, you can change the method code to provide your own research.
void main() { ArrayPChar AC1; ArrayPChar AC2 = AC1; // Copy constructor is called AC1.Print("AC1"); AC2.Print("AC2"); AC1.Add("Hello!"); AC1.Print("AC1"); AC1.Add("World"); // Check the copy constructor ArrayPChar AC3 = AC1; AC3.Print("AC3"); // Checking an overloaded assignment operator AC2 = AC3; AC2.Print("AC2"); // Check the function GetAi() char* s1; s1 = AC2.GetAi(0); cout << "s1 = " << s1 << endl; // Check the function SetAi() AC2.SetAi("ABCD", 1); AC2.Print("AC2"); // Check the function Del() AC2.Add("JKLMN"); AC2.Add("OPRST"); AC2.Print("AC2"); AC2.Del(3); AC2.Print("AC2-[3]"); // Checking a constructor that receives an array of type char** const char* Array[3] = { "Programming", "C++", "Strings" }; ArrayPChar AC4((char**)Array, 3); AC4.Print("AC4"); AC4.Add("Java"); AC4.Print("AC4"); }
⇑
5. The text of the entire program. Abridged version
#include <iostream> using namespace std; // Arrays of strings of type char* class ArrayPChar { private: // internal data char** A; // array of strings int length; // number of strings in an array of type char* // A method that determines if index is a valid array index. // The method returns true if the index value is correct. bool CheckIndex(int index) { ... } // Internal function - copies the string source to dest, // memory for dest is allocated in the middle of the function void CopyStr(char** dest, const char* source) { ... } // A function that copies the array of strings source to the array dest. // Memory for dest is allocated in the middle of the function void CopyArrayStr(char*** dest, char** source, int length) { ... } // Internal function that frees memory for the input array void Free(char** A, int length) { ... } public: // Constructors // Constructor without parameters ArrayPChar() { ... } // Constructor initializing internal data with the value of another array ArrayPChar(char** _A, int _length) { ... } // Copy constructor ArrayPChar(const ArrayPChar& _A) { ... } // Methods to access a separate string GetAi (), SetAi () // Reading a string at index i char* GetAi(int i) { ... } // Writing a string of type char * to string A[i] at index i, // The function returns true if the write was successful. bool SetAi(const char* str, int i) { ... } // --------------- String manipulation methods ---------------- // Add string to end of array void Add(const char* str) { ... } // Delete item from the given position index = 0, 1, ..., length-1 void Del(int index) { ... } // ---------------------------------------------------------- // Overloaded assignment operator ArrayPChar& operator=(ArrayPChar& _A) { ... } // Destructor ~ArrayPChar() { ... } // Method for outputting an array of strings to the screen, // the method receives a comment as a parameter, which is also displayed. void Print(const char* text) { ... } }; void main() { ... }
⇑
Related topics
- The string class. Examples of use
- Overloading the assignment operator =. Examples
- Copy constructor. Examples of using. Passing a class object to a function. Returning a class object from a function
- The concept of bitwise copying. Example
⇑