Types & Expressions 2

String

Today continues our focus on variable types by introducing our first non-built-in type called string.

string
The types we've seen so far (int, double, float, char, bool) are all built-in types. This means that they are part of the core language rather than part of some library. The last type we'll talk about today, the type string, is not a built-in type, it is defined in the library string. It is used for strings of characters.
We've already dealt with some string literals, for example "Hello World!", including the quotation marks, is a string literal. As long as you remember #include <string> (or include some other library that itself includes string, like iostream) and using namespace std;, you can use strings just like any other type. The true name of this type is std::string, but with the using statement we can drop the std::.

operation(s)Comments
+Concatenation. For example, if s is the string "Hello" and t is the string "World", then s + t is the string "HelloWorld".

Note: Strictly speaking, the literal "Hello World!" is not of string type but of some other built-in type (i.e., array of characters), about which we will learn later in this course. For now, you can safely ignore this subtlety; just be aware that the compiler will complain about this expression: "Hello" + "World". Of course, you can create a string variable from this literal, and the following code fragment will compile fine:

string s = "Hello";
string t = "World";
cout << s+t;

Other non-built-in types
In fact, there are many, many more types in C++. Even cin and cout are objects with types: cin is an object of type istream and cout is an object of type ostream. It will be a while before we discuss what these types are all about, but most things in C++ are objects with types.

The Power of Types

One of the easiest ways to see the importance of types in C++ is to watch how they affect cin. When you use cin >> x, the behavior of cin depends on the type of x. In other words, cin uses type information to do the right thing.
CodeUser InputProgram OutputUnread Input
char c;
cin >> c;
cout << c << endl;
23.005% 2 23.005%
int k;
cin >> k;
cout << k << endl;
23.005% 23 23.005%
double x;
cin >> x;
cout << x << endl;
23.005% 23.005 23.005%
string s;
cin >> s;
cout << s << endl;
23.005% 23.005% 23.005%
bool b;
cin >> b;
cout << b << endl;
23.005% 1 This results in an error state for cin. Only values of 1 or 0 with whitespace or end-of-file following will read in correctly. This, of course, doesn't fit the rules for the other type. Sigh. I'm trying to find out the rationale.
Look carefully at this table, and make sure that you understand why cin did what it did based on the types of the variables into which it read. NOTE: Any string tab, space, and newline characters is called whitespace. An important feature of cin is that it skips leading whitespace before it starts reading.

Input and Output as Streams of Characters

Input like
Jones   3:25
Smith   4:11
seems to be spread over several lines and composed of different elements - numbers, strings, and characters. However, it is in fact just one long line of characters, and by reading data we move through this line of characters called an input stream. Suppose, for example, we ran the code
string str1,str2;
int m1,m2,s1,s2;
char c1,c2;
cin >> str1 >> m1 >> c1 >> s1;
cin >> str2 >> m2 >> c2 >> s2;
with the above as input. What follows shows you this input stream, and demonstrates how we move through the input stream. The ^ carrot, shows what the "next character to be read" is. The tab character is represented by \t, and the newline character by \n.
   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                  ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                        ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                           ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                                 ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                                                      ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                                                            ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                                                               ^

   J  o  n  e  s  \t 3  :  2  5  \n    S  m  i  t  h  \t 4  :  1  1  \n
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
                                                                     ^

Everything's an expression ... almost

Almost everything in a C++ program is an expression. An expression is textual piece of source code that can be evaluated to produce an object that has a type and value. The most familiar expressions are arithmetical. For example, if k is an int that's been assigned the value 4,
(k - 2)*7
is an expression of type int and value 14.

One example of something that is less obviously an expression is:

k = 5
Here, k is once again a variable of type int. The type of this expression is int, and the value is 5.

In general, an assignment expression has the same type as the object being assigned to, and the same value as that object after the assignment is carried out. Thus, oddly enough, if x is a variable of type double, then

(x = 3.4)*2.0
makes perfect sense. That is, it is an expression of type double, and after it is evaluated, it has value 6.8. This is our first explicit example of a side effect. The expression has type and value, but additionally it has the side effect that evaluating the expression changes the value of the variable x.

Another example of something that probably doesn't seem like an expression but is in fact, is:

cout << x
As we saw last lecture, cout is an object of type ostream. The expression cout << x also has type ostream, and its value is just cout (like multiplying zero by anything, you still get zero). However, there is a side-effect to evaluating this expression, namely that x gets written out to the screen!

Note:

Often the value of an expression cannot be determined until the program is run. For example, Look at the code below:
int k;
cin >> k;
cout << k+7; 
What is the value of the expressioon k+1? Well, it depends what the user enters for input. That's something that cannot be known until the program is run.

However, the type of the expression k+1 is something we do know before running the program: it's int. Most of the time (especially for the subset of C++ we concentrate on) the type of an expression is known at compile time, i.e. when compiling the program, as opposed to running it. Because of this, C++ is said to be a "statically typed" language.

What's not an expression?

At this point it may seem like everything's an expression, but that's not true. For example: Still, most things are expressions, and understanding this fact and being able to identify the types and values of expressions are key to understanding C++ ... and most any other programming language.

When types collide - conversion

Implicit type conversion

Things get interesting when expressions involve different types. For example, what is the type and value of the expression x*k, where x is of type double with value 3.3, and k is of type int with value -2? The answer is type double and value -6.6. The explanation is this:

C++ knows how multiply two int objects, and it knows how to multiply two double objects, it doesn't know how to multiply one of each. However, it understands that an int can be converted to a double and vice versa. So it converts one and performs the multiplication on two objects of the same type. But which way should it go?

For arithmetic, types are always converted in the direction that gives the most precision - this is referred to as type promotion.
So, in our example above, the int is converted (or promoted) to a double, and the operation is performed on two doubles. It wouldn't make nearly as much sense the other way round, would it?

This implicit type conversion (implicit meaning that it happens automatically behind the scenes, without you doing anything directly) happens in other cases. The only one affecting us right now is assignments. You can assign an object of one type to an object of a different type, as long as C++ knows how to do the conversion. If it doesn't, the compiler will let you know. So, for example, x is of type double with value 3.3, and k is of type int, then k = x is an expression of type int with value 3. C++ truncates doubles when converting to ints.

Another way to do type conversion amongst the built-in types (int, double, char, bool) is to follow the C-language sytax, which is to preface the expression you are converting with the new type in parentheses. For example, if k is an int and you'd like to convert it to the equivalent double, you'd write:
(double)k
which would "cast" k to type double. C++ actually adds functionality to C, i.e. it is literally C plus some other stuff. So C constructs like (double)k all work.

Explicit type conversion

We also have explicit type conversion. Suppose, for example, that m and n are ints, n being the larger. We'd like to print out the value of m/n. Well,
cout << m/n << endl;
will just print out zero! (Make sure you know why!) We'd like to get some fractional value, in other words, we'd like these values treated as doubles. To explicitly convert them to doubles first we'd write:
cout << double(m)/double(n) << endl;
Challenge:

Can you explain what type and value you get with m / double(n)?

Explicit conversion can get tricky later on, but at this stage it's as simple as writing the new type name followed by the old object in ()'s.

Some Quick Conversion Rules
int → double : This does exactly what you'd expect.
double → int : This simply truncates the value, meaning that whatever's after the decimal point just gets chopped. You can get in trouble if the double value is too big.
bool → int : true goes to 1, false goes to 0.
int → bool : 0 goes to false, everything else goes to true;
int → char : if the int is in the range 0-127 the char value is determined by the ASCII table;
char → int : the int value is determined by the ASCII table;

Problems

Do You Know ...

  1. The difference between an "object" and a "type"?
  2. Why 1/2 is 0 in C++?
  3. What is a literal?
  4. What's the difference between a statement and expression?
  5. When does an implicit type conversion take place?
  6. How do you make an explicit type conversion?
  7. What will be the output of the following code?