The string Class and STL Introduction


The string Class

Using C-style (NULL-terminated) strings (static arrays).
#include <iostream>
#include <string>
using namespace std;

void Concat1(void)
{
  char s1[20];
  strcpy(s1, "The quick");
  strcat(s1, " brown fox");
  cout << s1 << endl;

  char s2[25];
  strcpy(s2, "jumped over");
  strcat(s2, " the lazy");
  strcat(s2, " dog");
  cout << s2 << endl;

  char s3[45];
  strcpy(s3, s1);
  strcat(s3, " ");
  strcat(s3, s2);
  cout << s3 << endl;
}
Output:
The quick brown fox
jumped over the lazy dog
The quick brown fox jumped over the lazy dog
Using C-style (NULL-terminated) strings (dynamic arrays):
#include <iostream>
#include <string>
using namespace std;

void Concat2(void)
{
  char *s1 = new char[20];
  strcpy(s1, "The quick");
  strcat(s1, " brown fox");
  cout << s1 << endl;

  char *s2 = new char[25];
  strcpy(s2, "jumped over");
  strcat(s2, " the lazy");
  strcat(s2, " dog");
  cout << s2 << endl;

  char *s3 = new char[45];
  strcpy(s3, s1);
  strcat(s3, " ");
  strcat(s3, s2);
  cout << s3 << endl;

  delete[] s1;
  delete[] s2;
  delete[] s3;
}
Output:
The quick brown fox
jumped over the lazy dog
The quick brown fox jumped over the lazy dog
Using the string class:
#include <iostream>
#include <string>
using namespace std;

void Concat3(void)
{
  string s1 = "The quick";
  s1 += " brown fox";
  cout << s1 << endl;

  string s2;
  s2 = s2 + "jumped over" + " the lazy" + " dog";
  cout << s2 << endl;

  string s3 = s1 + " " + s2;
  cout << s3 << endl;
}
Output:
The quick brown fox
jumped over the lazy dog
The quick brown fox jumped over the lazy dog

Constructors for the string class:

void Construct(void)
{
    // Create an empty string
  string s0;
  cout << s0 << endl;

    // Create a string from a const char *
  string s1("This is a string");
  cout << s1 << endl;

    // Create a string of chars
  string s2(10, '#');
  cout << s2 << endl;

    // Create a string from another string (copy constructor)
  string s3(s1);
  cout << s3 << endl;

    // Create a string from a char * and a count
  const char *p = "The quick brown fox";
  string s4(p, 9);
  cout << s4 << endl;

    // Create a string from a char * and a count
  string s5(p + 4, 5);
  cout << s5 << endl;

    // Create a string from two pointers (between)
  string s6(p + 10, p + 15);
  cout << "|" << s6 << "|" << endl;
}
Output:

This is a string
##########
This is a string
The quick
quick
|brown|

string Input

void Input(void)
{
  string s1;
  cout << "Enter a word: ";

    // Read one "word"
  cin >> s1;

  cout << s1 << endl;

    // Ignore the rest of the line
  while (cin.get() != '\n')
    continue;

  string s2;
  cout << "Enter several words: ";

    // Read up to the new line
  getline(cin, s2);
  cout << s2 << endl;
}
Output:
Enter a word: Hello
Hello
Enter several words: One two three four.
One two three four.

Fixing the bug in Visual C++

On line 165 of the file ..\VC98\Include\STRING you need to change :

_I.rdbuf()->snextc();
to
_I.rdbuf()->sbumpc();
Here's a web page that has info on the getline fix.

Other string Methods

void Test1(void)
{
  string s1 = "DigiPen";

    // Reading the length of a string
  cout << "Length of " << s1 << " is " << s1.length() << endl;

    // You can access the internal NULL terminated string
  cout << "Length of " << s1.c_str() << " is " << strlen(s1.c_str()) << endl;

    // Getting at individual characters
  cout << "The second char is: " << s1.at(1) << endl;
  cout << "The second char is: " << s1[1] << endl;

    // Finding substrings
  int position = s1.find("Pen");
  if (position != string::npos)
    cout << s1.substr(position) << endl;
}
Output:
Length of DigiPen is 7
Length of DigiPen is 7
The second char is: i
The second char is: i
Pen

There are lots of methods available in the string class to do things such as: The string class is just a typedef a templatized basic_string class:
template <class CharType, class Attr = char_traits<CharType>, class Allocator = allocator<CharType> >
class basic_string
{
  // ...
};

typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
Here's the MSDN help page on basic_string.

The Standard Template Library

Creating a simple vector of integers and then resizing the vector:
void VecStuff(void)
{
  int SIZE = 5;
  vector<int> numbers(SIZE);       // create a vector of integers (5)
  for (int i = 0; i < SIZE; i++)   // assign values to each element
    numbers[i] = i * 10;

  numbers.resize(numbers.size() * 2);  // "grow" the vector twice as big (10)
  for (i = SIZE; i < SIZE * 2; i++)    // assign values to the new elements
    numbers[i] = i * 100;

  for (i = 0; i < SIZE * 2; i++)       // print out all elements
    cout << numbers[i] << endl;
}
Output:
0
10
20
30
40
500
600
700
800
900
Creating a vector of strings:
void Vector2(void)
{
  int SIZE = 6;
  vector<string> speeds(SIZE);      // create a vector of strings (6)

  speeds[0] = "Snail (slowest)";    // assign values to each element
  speeds[1] = "Turtle (slower)";
  speeds[2] = "Penguin (slow)";
  speeds[3] = "Rabbit (fast)";
  speeds[4] = "Lion (faster)";
  speeds[5] = "Cheetah (fastest)";

    // Display each speed that contains the substring "fast"
  for (int i = 0; i < SIZE; i++)
    if (speeds[i].find("fast") != string::npos)
      cout << speeds[i] << endl;

}
Output:
Rabbit (fast)
Lion (faster)
Cheetah (fastest)
Here's the MSDN help page on vector.


Modifying the Polygon class to use a vector instead of an array.

Current implementation:

#include "Shape.h"

class Polygon : public Shape
{
  private:
    Point *m_Vertices;  // array of Points
    int m_NumVertices;  // number of Points

  public:
      // public methods
};
Proposed implementation:
#include "Shape.h"
#include <vector>

class Polygon : public Shape
{
  private:
    vector<Point> m_Vertices;  // vector of Points

  public:
      // public methods
};
Polygon using vectors.


An old constructor:

Polygon::Polygon(const Point *Vertices, int NumVertices)
{
  m_NumVertices = NumVertices;
  m_Vertices = new Point[m_NumVertices];
  for (int i = 0; i < m_NumVertices; i++)
    m_Vertices[i] = Vertices[i];
}
A new constructor:
Polygon::Polygon(const Point *Vertices, int NumVertices)
{
  m_Vertices.resize(NumVertices);
  for (int i = 0; i < m_Vertices.size(); i++)
    m_Vertices[i] = Vertices[i];
}

The old Move method:

void Polygon::Move(const Point &position)
{
  for (int i = 0; i < m_NumVertices; i++)
    m_Vertices[i] = m_Vertices[i] + position;
}
The new Move method:
void Polygon::Move(const Point &position)
{
  for (int i = 0; i < m_Vertices.size(); i++)
    m_Vertices[i] = m_Vertices[i] + position;
}

Using an iterator instead of a for loop:

ostream & operator<<(ostream & os, const Polygon &polygon)
{
  os << "NumVertices: " << polygon.m_Vertices.size();
  vector<Point>::const_iterator it;
  for (it = polygon.m_Vertices.begin(); it != polygon.m_Vertices.end(); it++)
    os << ", " << *it;
  return os;
}
void Polygon::Move(const Point &position)
{
  vector<Point>::iterator it;
  for (it = m_Vertices.begin(); it != m_Vertices.end(); it++)
    *it = *it + position;
}

Using push_back:

void Polygon::SetVertices(const Point vertices[], int NumVertices)
{
  m_Vertices.clear();
  m_Vertices.resize(NumVertices);

  for (int i = 0; i < NumVertices; i++)
    m_Vertices[i] = vertices[i];
}
void Polygon::SetVertices(const Point vertices[], int NumVertices)
{
  m_Vertices.clear();
  for (int i = 0; i < NumVertices; i++)
    m_Vertices.push_back(vertices[i]);
}

Constant-time vs. linear-time operations (Algorithm complexity)

Introduction to Using Generic Algorithms

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void Print(int value)
{
  cout << value << " ";
}

void PrintVec(vector<int> vec)
{
  vector<int>::const_iterator iter;
  for (iter = vec.begin(); iter != vec.end(); iter++)
    Print(*iter);

  cout << endl;
}

void main(void)
{
    // Create an empty vector of integers
  vector<int> nums;

    // Put 10 random integers in the vector
  for (int i = 0; i < 10; i++)
    nums.push_back(rand() % 100);

    // Pass the vector to a function to print
  PrintVec(nums);

    // Sort the vector
  sort(nums.begin(), nums.end());

    // Pass each element of the vector to a function to print
  for_each(nums.begin(), nums.end(), Print);  
  
}

Output:
41 67 34 0 69 24 78 58 62 64
0 24 34 41 58 62 64 67 69 78

Using Templates


include <iostream>
include <vector>
include <algorithm>
using namespace std;

template <typename T>
void Print(T value)
{
  cout << value << " ";
}

template <typename T>
void PrintVec(vector<T> vec)
{
  vector<T>::const_iterator iter;
  for (iter = vec.begin(); iter != vec.end(); iter++)
    Print(*iter);

  cout << endl;
}

void main(void)
{
    // Create an empty vector of integers
  vector<int> nums;

    // Put 10 random integers in the vector
  for (int i = 0; i < 10; i++)
    nums.push_back(rand() % 100);

    // Pass the vector to a template function to print
  PrintVec(nums);

    // Sort the vector
  sort(nums.begin(), nums.end());

    // Pass each element of the vector to a template function to print
  for_each(nums.begin(), nums.end(), Print<int>);
}

Output:
41 67 34 0 69 24 78 58 62 64
0 24 34 41 58 62 64 67 69 78
Since the PrintVec function is redundant (due to the for_each function) we can just remove it. However, the following code won't compile:
template <typename T>
void Print(T value)
{
  cout << value << " ";
}

void main(void)
{
    // Create an empty vector of integers
  vector<int> nums;

    // Put 10 random integers in the vector
  for (int i = 0; i < 10; i++)
    nums.push_back(rand() % 100);

    // Pass each element of the vector to a template function to print
  for_each(nums.begin(), nums.end(), Print<int>);
  cout << endl;

    // Sort the vector
  sort(nums.begin(), nums.end());

    // Pass each element of the vector to a template function to print
  for_each(nums.begin(), nums.end(), Print<int>);
  cout << endl;
}
We need to explicitly instantiate the version of the template we need:
template <typename T>
void Print(T value)
{
  cout << value << " ";
}

template void Print<int>(int);  // explicit instantiation

void main(void)
{
  ...
}
Why did it work with the PrintVec function?

Client code using a vector of string:

template void Print<string>(string);

void main(void)
{
    // Create an empty vector of integers
  vector<string> critters;

  critters.push_back("Snail");   
  critters.push_back("Turtle");
  critters.push_back("Penguin");
  critters.push_back("Rabbit");
  critters.push_back("Lion");
  critters.push_back("Cheetah");

    // Pass each element of the vector to a template function to print
  for_each(critters.begin(), critters.end(), Print<string>);
  cout << endl;

    // Sort the vector
  sort(critters.begin(), critters.end());

    // Pass each element of the vector to a template function to print
  for_each(critters.begin(), critters.end(), Print<string>);
  cout << endl;
}
Output:
Snail Turtle Penguin Rabbit Lion Cheetah
Cheetah Lion Penguin Rabbit Snail Turtle