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?
int main()
{
Person pers1("Tod");
char *p = pers1.getName(); // ERROR. Why?
p[0] = 'R';
cout << p << endl;
return 0;
}
int main()
{
Person pers1("Tod");
const char *p = pers1.getName();
p[0] = 'R'; // ERROR; Why?
cout << p << endl;
return 0;
}
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
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();
};
Implementation of second constructor and the destructor:
Employee::Employee(char nm[], double sal)
{
name = new char[strlen(nm) + 1];
strcpy(name, nm);
salary = sal;
}
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;
}
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;
}
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 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
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
- no copy constructor
- no destructor
- no operator=
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?
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:
- s.find(str) // start search at position 0 of s
- s.find(str, 0) // start search at position 0 of s
- s.find(str, 4) // start search at position 4 of s
- s.find_first_of(",;") // find position of first comma or semicolon
- s.find_first_of(",;", 4) // start at position 4
- s.find_first_not_of(",;") // find position of first
character not a comma and not a semicolon, starting at position 0
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.
- s.substr(0,4) // return a copy of 4 characters begining at
position 0 of s
- s.substr(3) // return a copy of all the
characters in s starting at position 3
- s.substr(3, string::npos) // return a copy of all the
characters in s starting at position 3
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.
#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
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
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:
- Getting string length faster
- Assignment of long strings faster (except slower with
Microsoft compiler)
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
- printf - output to standard output
- fprintf - output to a file
- sprintf - output to a string
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.
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
- an integer specifying the number of output character
positions to use (sort of like setw for c++ strings)
If the value needs fewer character positions, the value
is right justified.
However, if the integer is negative, the number of
character positions used is the absolute value and the value is left
justified.
Point p(3,4);
char str[15];
sprintf(str, "(%d, %d)", p.getX(), p.getY());
cout << str << endl;
Output:
(3,4)
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);
}
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;
}