C++. Developing a class that implements an array of char* strings




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


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