9. Fundamentals of C++ Strings

C++ Strings

In this note, we explore how C++ strings work, covering topics such as size, length, capacity, max_size, and various string operations like insert, push_back, pop_back, compare, and more.

1. Overview of C++ Strings

In C++, the std::string class (defined in the <string> header) offers a convenient way to manipulate sequences of characters. Unlike C-style character arrays (char*), std::string manages memory automatically, resizing when needed and keeping track of its own length. This provides both convenience and safety compared to traditional C-strings.

2. Fundamental Properties of std::string

2.1. size() vs. length()

2.2. capacity()

2.3. max_size()

3. Basic String Operations

3.1. Creating and Initializing Strings

#include <iostream>
#include <string>
using namespace std;

int main() {
    // Direct initialization
    string text = "Hello world!";
    cout << "Text: " << text << endl;

    // Creating a string with a specific number of repeated characters
    string stars(50, '*');
    cout << "Stars: " << stars << endl;

    return 0;
}

Output:

Text: Hello world!
Stars: **************************************************

3.2. resize() and shrink_to_fit()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s(50, '*'); // Creates a string with 50 '*'
    cout << s << endl;
    cout << "Capacity before resize: " << s.capacity() << endl;

    // Resize down to 10 characters
    s.resize(10);
    // Shrink the capacity to match the new size
    s.shrink_to_fit();

    cout << s << endl;
    cout << "Capacity after shrink_to_fit: " << s.capacity() << endl;

    return 0;
}

Example Output:

**************************************************
Capacity before resize: 63
**********
Capacity after shrink_to_fit: 15

3.3. reserve()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Apple";
    cout << "Initial capacity: " << text.capacity() << endl;

    text.reserve(1000);
    cout << "Capacity after reserve(1000): " << text.capacity() << endl;

    return 0;
}

Example Output:

Initial capacity: 15
Capacity after reserve(1000): 1007

3.4. clear() and empty()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Happy programmers day";
    cout << text << endl;  // Output: Happy programmers day

    text.clear();          // Clears the string

    if (text.empty()) {
        cout << "No Text" << endl;  // Output: No Text
    } else {
        cout << text << endl;
    }

    return 0;
}

4. Appending and Inserting

4.1. append() vs. operator+=()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Hi all";
    cout << text << endl;          // Output: Hi all

    // Using operator+=
    text += " , bye bye";
    cout << text << endl;          // Output: Hi all , bye bye

    // Using append()
    text.append("Salam millet");
    cout << text << endl;          // Output: Hi all , bye byeSalam millet

    return 0;
}

4.2. insert()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Happy programmers day";
    cout << text << endl;

    // Insert 'E' at the beginning
    text.insert(text.begin(), 'E');
    cout << text << endl;

    // Insert 'E' after the 5th character
    text.insert(text.begin() + 5, 'E');
    cout << text << endl;

    // Insert 'A' before the last character
    text.insert(text.end() - 1, 'A');
    cout << text << endl;

    return 0;
}

Example Output:

Happy programmers day
EHappy programmers day
EHappEy programmers day
EHappEy programmers daAy

5. Accessing String Characters

5.1. Index Access and at()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Happy";
    cout << text[0] << endl;      // H
    cout << text.at(0) << endl;   // H

    text[0] = 'Z';
    cout << text << endl;         // Zappy

    // Using a for loop to print each character
    for (int i = 0; i < text.size(); i++) {
        cout << text[i] << endl;
    }

    return 0;
}

5.2. front() and back()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Happy";
    cout << text.front() << endl; // H

    text.front() = 'P';
    cout << text.front() << endl; // P

    cout << text.back() << endl;  // y
    text.back() = 'i';
    cout << text.back() << endl;  // i

    cout << text << endl;         // Pappi

    return 0;
}

6. Other Common Methods

6.1. substr()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Happy programmers day";
    // From index 6, get 10 characters
    cout << text.substr(6, 10) << endl;    // programmer
    cout << text << endl;                  // Happy programmers day

    return 0;
}

6.2. pop_back() and push_back()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "SALAM SALAM";

    text.pop_back();     // Removes the last character
    cout << text << endl;  // SALAM SALA

    text.push_back('Z'); // Adds 'Z' to the end
    cout << text << endl;  // SALAM SALAZ

    return 0;
}

6.3. compare()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text1 = "salam";
    string text2 = "Salam";

    cout << boolalpha << (text1 == text2) << endl;    // false
    cout << text1.compare(text2) << endl;             // 1 (or a positive number)

    return 0;
}

6.4. assign()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text;
    text = "Salam";
    cout << text << endl;

    string text2;
    text2.assign("Salam");
    cout << text2 << endl;

    return 0;
}

6.5. find() and rfind()

#include <iostream>
#include <string>
using namespace std;

int main() {
    string text = "Happy programmers day";
    // find() returns the index of first occurrence, or string::npos if not found
    cout << text.find('j') << endl;   // Might return a large number if 'j' isn't found
    cout << text.find('H') << endl;   // 0 if 'H' is the first character

    // rfind() returns the index of the last occurrence
    cout << text.rfind('j') << endl;
    cout << text.rfind('y') << endl;

    return 0;
}

7. Complete Example: Putting It All Together

Below is a complete code example demonstrating multiple string operations in one program:

#include <iostream>
#include <string>
using namespace std;

int main() {
    // 1. Create and initialize a string
    string text = "Hello C++ World!";
    cout << "Initial text: " << text << endl;

    // 2. Check size and capacity
    cout << "Size: " << text.size() << endl;
    cout << "Length: " << text.length() << endl;
    cout << "Capacity: " << text.capacity() << endl;
    cout << "Max Size: " << text.max_size() << endl;
    cout << "------------------------" << endl;

    // 3. Modify the string with += and append
    text += " Let's explore strings.";
    text.append(" Indeed!");
    cout << "After appending: " << text << endl;
    cout << "Size now: " << text.size() << endl;
    cout << "Capacity now: " << text.capacity() << endl;
    cout << "------------------------" << endl;

    // 4. Use reserve to pre-allocate memory
    text.reserve(100);
    cout << "Capacity after reserve(100): " << text.capacity() << endl;
    cout << "------------------------" << endl;

    // 5. Using front(), back(), and at()
    cout << "First character (front): " << text.front() << endl;
    cout << "Last character (back): " << text.back() << endl;
    cout << "Third character (at(2)): " << text.at(2) << endl;
    cout << "------------------------" << endl;

    // 6. Insert characters
    text.insert(text.begin(), 'X');
    text.insert(text.begin() + 5, 'Y');
    cout << "After insertions: " << text << endl;
    cout << "------------------------" << endl;

    // 7. pop_back() and push_back()
    text.pop_back();
    text.push_back('Z');
    cout << "After pop_back and push_back: " << text << endl;
    cout << "------------------------" << endl;

    // 8. substr()
    cout << "Substring (index 7, length 10): " << text.substr(7, 10) << endl;
    cout << "------------------------" << endl;

    // 9. compare
    string sample = "XHello";
    // Compare the first 6 characters of text with sample
    cout << "Compare first 6 chars of text to sample: "
         << text.compare(0, 6, sample) << endl;

    // 10. find and rfind
    cout << "find('C'): " << text.find('C') << endl;
    cout << "rfind('C'): " << text.rfind('C') << endl;
    cout << "------------------------" << endl;

    // 11. clear and check empty
    text.clear();
    if (text.empty()) {
        cout << "text is now empty after clear()" << endl;
    }

    return 0;
}

Possible Output (Will Vary Depending on Implementation)

Initial text: Hello C++ World!
Size: 16
Length: 16
Capacity: 15
Max Size: 2147483647
------------------------
After appending: Hello C++ World! Let's explore strings. Indeed!
Size now: 49
Capacity now: 63
------------------------
Capacity after reserve(100): 100
------------------------
First character (front): H
Last character (back): !
Third character (at(2)): l
------------------------
After insertions: XHellYHello C++ World! Let's explore strings. Indeed!
------------------------
After pop_back and push_back: XHellYHello C++ World! Let's explore strings. IndeedZ
------------------------
Substring (index 7, length 10): llo C++ Wo
------------------------
Compare first 6 chars of text to sample: 1
find('C'): 12
rfind('C'): 12
------------------------
text is now empty after clear()

8. Additional Notes and Best Practices

  1. Avoid Out-of-Range Access:
    • Using text.at(index) throws an exception if index is out of bounds.
    • Using text[index] where index is out of bounds can lead to undefined behavior.
  1. Efficient Concatenation:
    • If you plan multiple concatenations, consider using reserve() to minimize reallocation overhead.
  1. Comparisons:
    • == is straightforward for equality. For greater or less comparisons, use compare().
  1. Memory:
    • While std::string simplifies memory management, always be mindful of large strings. max_size() helps to identify potential limits.
  1. Iterators:
    • Many string operations use iterators (begin(), end()), which are powerful for generic code that might also apply to other containers.

Reference

The content in this document is based on the original notes provided in Azerbaijani. For further details, you can refer to the original document using the following link:

Original Note - Azerbaijani Version