Today continues our focus on variable types by introducing our first non-built-in type called string.
string
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.
"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;
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.
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.
Code | User Input | Program Output | Unread 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. |
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.
Jones 3:25 Smith 4:11seems 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 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ^
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
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
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!
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.
k = 4
is an expression as used
in (k=4)*3
will evaluate to 12, the statement k =
4;
as a line of code ending in a semicolon is not an expression.
int k
for example, are not expressions - regardless of whether the
; is there.
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?
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
double
s when converting to int
s.
k
is an int
and you'd like to
convert it to the equivalent double
, you'd write:
(double)kwhich 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.
m
and n
are int
s,
n
being the larger. We'd like to print out the value of
m/n
. Well,
cout << m/n << endl;
double
s. To explicitly convert them to double
s
first we'd write:
cout << double(m)/double(n) << endl;
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; |