CSC262 Feb08

slide version

single file version

Contents

  1. C Strings: As Class Members (1)
  2. A main Program Using the Person Class
  3. Revised main
  4. Final Revision
  5. C Strings: As Class Members (2)
  6. Constructor
  7. Rule of 3, part 1: Destructor
  8. Rule of 3, part 2: Copy Constructor
  9. Rule of 3, part 3: Assignment Operator
  10. C++ Strings
  11. Employee Class with C++ string Member
  12. C++ string: find member functions
  13. C++ string: substr
  14. C++ string Example
  15. C++ string Example 2
  16. Advantages of C++ string over C-strings
  17. C I/O and Conversions to Strings
  18. The printf functions
  19. Example
  20. Convert a C++ string to a C string
  21. Convert a C string to a C++ string

C Strings: As Class Members (1)[1] [top]

Here is a very simple class with a C-string member:


class Person
{
  char name[50];
public:
  Person(const char nm[]);
  const char * getName() const;
};

In the Person.cpp file, the implementations are


Person::Person(const char nm[])
{
  strcpy(name, nm);
}

const char * Person::getName() const
{
  return name;
}

What restrictions do all the const qualifiers imply?

A main Program Using the Person Class[2] [top]


int main()
{
  Person pers1("Tod");

  char *p = pers1.getName(); // ERROR. Why?

  p[0] = 'R';
  
  cout << p << endl;

  return 0;
}

Revised main[3] [top]


int main()
{
  Person pers1("Tod");

  const char *p = pers1.getName(); 

  p[0] = 'R'; // ERROR; Why?
  
  cout << p << endl;

  return 0;
}

Final Revision[4] [top]


int main()
{
  Person pers1("Tod");

  const char *p = pers1.getName(); 

  // p[0] = 'R'; Can't change pers1.name by this trick
  
  cout << p << endl;

  return 0;
}
OUTPUT:
Tod

C Strings: As Class Members (2)[5] [top]


class Employee
{
  char *name;
  double salary;
public:
  Employee();
  Employee(char nm[], double sal);
  Employee(const Employee& other);

    
  const char * getName() const;
  double getSalary() const;

  void setSalary(double newSalary);

  void operator=(const Employee& other);

  ~Employee();
};

Constructor[6] [top]

Implementation of second constructor and the destructor:


Employee::Employee(char nm[], double sal)
{
  name = new char[strlen(nm) + 1];
  strcpy(name, nm);
  salary = sal;
}
    

Rule of 3, part 1: Destructor[7] [top]

Since the constructor dynamically allocates a C-string on the heap with new this memory needs to be deallocated when the Employee value itself is deallocated.

This is easy. Just write a destructor to delete the memory:


Employee::~Employee()
{
  delete [] name;
}
    

Rule of 3, part 2: Copy Constructor[8] [top]

The Employee copy constructor is a constructor whose parameter is another Employee. This other Employee is used to initialize the data members of this Employee.

The copy constructor will be used by the compiler to make a copy when an Employee variable is passed by value (or returned by value).


Employee::Employee(const Employee& other)
{
  name = new char[strlen(other.name) + 1];
  strcpy(name, other.name);
  salary = other.salary;
}

Rule of 3, part 3: Assignment Operator[9] [top]

When one Employee variable is assigned to another, the compiler makes a copy, but it does not use the copy constructor!

The copy constructor is not sufficient because, the old value of the Employee must be deleted in an assignment to avoid a memory leak.

The assignment operator= differs from the copy constructor only because of this deletion.


void Employee::operator=(const Employee& other)
{
  if (  other == &this ) { // What?
    return;
  }
  delete [] name;
  // rest is the same as the copy constructor
  name = new char[strlen(other.name) + 1];
  strcpy(name, other.name);
  salary = other.salary;
}

C++ Strings[10] [top]

C++ strings are a new type: string.

This type is defined by the string class.

There are many member functions and also many overloaded non-member operators.

The string class has its own assignment operator, copy constructor, and destructor. So in general, you don't have to manage memory for C++ strings

Employee Class with C++ string Member[11] [top]

class Employee
{
  string name;
  double salary;
public:
  Employee();
  Employee(const string& nm, double sal);
    
  string getName() const;
  double getSalary() const;

  void setSalary(double newSalary);

};
    

Rule of 3 doesn't apply. So this Employee class needs

How is an Employee variable passed by value? That is, how is a copy made?

How is one Employee variable assigned to another? Does the old employee name get deleted?

When the Employee itself is deallocated, does the string name also get deallocated?

C++ string: find member functions[12] [top]

The find member of the string class finds the position in a string of another string or character. There are several versions of find. E.g. to find a string str as a substring of a string s:

Note 1: The position of the first character of a string is 0.

Note 2: If the string or character specified is not found, the return value is a special value defined in the string class:


string::npos
      
    

This value is the largest (unsigned) integer value.

C++ string: substr[13] [top]

Return a copy of a substring of a string.

First parameter is the starting position and second parameter is the number of characters to copy.

But if the second parameter is greater than the remaining characters in the string, it isn't an error. It just means copy all the remaining characters.

The starting position must be a valid position in the string; that is, the start position, p, must satisfy:

0 <= p < s.length()      
    

The length parameter can be larger than the number of characters remaining from the starting position.

C++ string Example[14] [top]


#include <iostream>
#include <string>
#include <cstdlib>

int main()
{
  string s = "Brian W. Kernighan;    The Practice of Programming;
    Addison Wesley";
  string f;
  size_t start, after;

  start = s.find_first_not_of(" \t;");
  after = s.find(";", start);
  cout << s.substr(start, after - start) << endl;

  return 0;
  
}

Output: 
Brian W. Kernighan

C++ string Example 2[15] [top]

Problem: Find and print on separate lines all parts of the string separated by semicolons.


#include <iostream>
#include <string>
#include <cstdlib>

int main()
{
  string s = "Brian W. Kernighan;   The Practice of Programming; Addison Wesley";
  string f;
  size_t start, after;

  start = s.find_first_not_of(" \t;");
  while ( start != string::npos ) {
    after = s.find(";", start);  // set to string::npos if not found
    f = s.substr(start, after - start);
    cout << f << endl;
    start = s.find_first_not_of(" \t;", after);
  }

  return 0;
}

Output:
Brian W. Kernighan
The Practice of Programming
Addison Wesley

Advantages of C++ string over C-strings[16] [top]

Efficiency results are mixed and sometimes depends on the particular compiler used.

Memory management for C-strings is the responsibility of the programmer; C++ string class manages memory for the characters in a C++ string.

Other possible advantages for C++ string over C-strings:

C I/O and Conversions to Strings[17] [top]

The new stream classes (iostream, fstream, sstream) can be used convert data to a C++ string for output to standard output, a file, or to a string.

Is this efficient? Are there alternatives?

The C I/O library also can be used to convert data to C-strings

The output C functions are

The sprintf function is typically much faster than using a stringstream!

On the other hand, sprintf can only be used with basic types, not class types. While stringstream can be used with any class type, provided operator<< has been overloaded for values of that class type.

On the other, other hand, conversions to string of complicated classes may not be needed in many kinds of applications.

The printf functions[18] [top]

 int printf(const char format_str[], ...)
 int fprintf(FILE *fp, const char format_str[], ...)
 int sprintf(char output_str[], const char format_str[], ...)
    

The format string can contain 0 or more format specifications mixed with ordinary characters to be output.

Format specifications are replaced by a converted value.

For each format specification, the parameter list should contain a value to be converted and inserted in place of the format spec.

Some format specifications

Format Specifier Meaning
%d Convert an integer value (to decimal using digits 0 - 9)
%x Convert an integer value (to hex using 0 - 9 and A - F)
%s "convert" a string value (really no conversion needed)

Between the percent sign and the conversion letter, you can have

Example[19] [top]

      Point p(3,4);  
      char str[15];

      sprintf(str, "(%d, %d)", p.getX(), p.getY());

      cout << str << endl;
      
Output:
(3,4)
    

Convert a C++ string to a C string[20] [top]

Why? Because some functions require a C string, not a C++ string. For example, to open an ifstream requires the file name be a C-string.

How?

int main() { string fname; char cfname[130]; cout << "Enter a file name (no spaces): "; cin >> fname; strcpy(cfname, fname.c_str()); ifstream ifs; ifs.open(cfname); // NOT ifs.open(fname); }

Convert a C string to a C++ string[21] [top]

The string class takes care of this with its constructors and/or its overloaded operator=


#include <iostream>
#include <string>
#include <cstdlib>

int main()
{
   char cname[] = "Bob";

   string name;

   name = "Bob";

   // or

   name = cname;

}