#include using namespace std; class BaseString { protected: char* p; int len; int capacity; public: explicit BaseString(int Capacity = 256) // mark Single-argument constructors explicit to avoid unintentional implicit conversions { cout << "\nBase Constructor 0\n"; capacity = Capacity; p = new char[capacity]; len = 0; } explicit BaseString(const char* ptr) //create object from input string // mark Single-argument constructors explicit to avoid unintentional implicit conversions { cout << "\nBase Constructor 1\n"; len = Length(ptr); capacity = len; // allocate needed memory dynamically based on the Length of the input. //avoid unnecessary memory allocation. p = new char[capacity]; CopyString(p, ptr, len); } ~BaseString() { cout << "\nBase Destructor\n"; delete[] p; len = 0; } static int Length(const char* str) //static because method is not using object state and don`t need access to class members { int length = 0; while (*str != '\0') { length++; str++; } return length; } static void CopyString(char* destination, const char* source, int length) //static because method is not using object state and don`t need access to class members { while (length--) { *destination++ = *source++; } *destination = '\0'; } int Length() const { return len; } int Capacity() const { return capacity; } char& operator[](int i) { return p[i]; } BaseString& operator=(const BaseString& s) { cout << "\nBase Operator = \n"; if (this != &s) // self-copy||self-assignment checking. { char* new_p = new char[s.capacity]; CopyString(new_p, s.p, s.len); delete[] p; p = new_p; len = s.Length(); // not using len = s.len because len may not be actual. just in case. capacity = s.capacity; } return *this; } BaseString(const BaseString& s) // copy constructor // using const can avoid unnecessary copying of objects { cout << "\nBase Copy Constructor\n"; len = s.Length(); p = new char[s.capacity]; capacity = s.capacity; CopyString(p, s.p, len); } virtual void print() const { for (int i = 0; i < len; i++) { cout << p[i]; } } }; class DerivedString : public BaseString { public: explicit DerivedString(const char* ptr) : BaseString(ptr)// mark Single-argument constructors explicit to avoid unintentional implicit conversions { cout << "\nDerived Constructor\n"; } explicit DerivedString(int Capacity = 256) : BaseString(Capacity) // mark Single-argument constructors explicit to avoid unintentional implicit conversions { cout << "\nDerived Constructor 0\n"; } DerivedString& operator=(const DerivedString& s) { cout << "\nDerived Operator =\n"; if (this != &s) // self-copy||self-assignment checking. { BaseString::operator=(s); } return *this; } DerivedString(const DerivedString& s) : BaseString(s) // using const can avoid unnecessary copying of objects { cout << "\nDerived Copy Constructor\n"; } void print() const override { cout << "Derived: "; BaseString::print(); } int LastIndexOf(const char* substr) const { int substrLen = BaseString::Length(substr); int startPos = len; // Start searching from the end of the string while (startPos >= 0) { int i = 0; while (i < substrLen && p[startPos + i] == substr[i]) { i++; } if (i == substrLen) // If the entire substring matches { return startPos; } startPos--; // Move to the previous position } return -1; // Not found } }; int main() { char str[] = "Hello, world!"; // creating objects of BaseString and calling functions cout << "Creating and manipulating BaseString object:\n"; BaseString aa(str); aa.print(); cout << "\nLength: " << aa.Length() << endl; cout << "Capacity: " << aa.Capacity() << endl; cout << "Character at index 1: " << aa[1] << endl; // creating objects of DerivedString and calling functions cout << "\nCreating and manipulating DerivedString object:\n"; DerivedString a(str); DerivedString b; b = a; // calling operator = b.print(); cout << "\nLength: " << b.Length() << endl; cout << "Capacity: " << b.Capacity() << endl; cout << "Character at index 2: " << b[2] << endl; //LastIndexOf const char* substr = "wo"; int lastIndex = a.LastIndexOf(substr); if (lastIndex != -1) { cout << "Last index of '" << substr << "': " << lastIndex << endl; } else { cout << "Substring '" << substr << "' not found." << endl; } return 0; }