You are on page 1of 5

C++ names are case-sensitive, and the first 2,048 characters are significant.

You
can start a
variable name with an underscore, but you cannot use two underscores, nor can you
use an
underscore followed by a capital letter (these are reserved by C++). C++ also
reserves
keywords (for example, while and if), and clearly you cannot use type names as
variable
names, neither built in type names (int, long, and so on) nor your own custom
types.
You declare a variable in a statement, ending with a semicolon. The basic syntax of
declaring a variable is that you specify the type, then the name, and, optionally,
any
initialization of the variable.
Built-in types must be initialized before you use them:
int i;
i++; // C4700 uninitialized local variable 'i' used
std::cout << i;
There are essentially three ways to initialize variables. You can assign a value,
you can call
the type constructor (constructors for classes will be defined in Chapter 6,
Classes) or you
can initialize a variable using function syntax:
int i = 1;
int j = int(2);
int k(3);
Understanding Language Features
[ 55 ]
These three are all legal C++, but stylistically the first is the better because it
is more
obvious: the variable is an integer, it is called i, and it is assigned a value of
1. The third
looks confusing; it looks like the declaration of a function when it is actually
declaring a
variable. The next chapter will show a variation of assigning a value using the
initialization
list syntax. The reasons why you will want to do this will be left to that chapter.
Chapter 6, Classes will cover classes, your own custom types. A custom type may be
defined to have a default value, which means that you may decide not to initialize
a
variable of a custom type before using it. However, this will result in poorer
performance,
because the compiler will initialize the variable with the default value and
subsequently
your code will assign a value, resulting in an assignment being performed twice.
Using constants and literals
Each type will have a literal representation. An integer will be a numeric
represented
without a decimal point and, if it is a signed integer, the literal can also use
the plus or
minus symbol to indicate the sign. Similarly, a real number can have a literal
value that
contains a decimal point, and you may even use the scientific (or engineering)
format
including an exponent. C++ has various rules to use when specifying literals in
code, and
these will be covered in the next chapter. Some examples of literals are shown
here:
int pos = +1;
int neg = -1;
double micro = 1e-6;
double unit = 1.;
std::string name = "Richard";
Note that for the unit variable, the compiler knows that the literal is a real
number because
the value has a decimal point. For integers, you can provide a hexadecimal literal
in your
code by prefixing the number with 0x, so 0x100 is 256 in decimal. By default, the
output
stream will print numeric values in base 10; however, you can insert a manipulator
into an
output stream to tell it to use a different number base. The default behavior is
std::dec,
which means the numbers should be displayed as base 10, std::oct means display as
octal
(base 8), and std::hex means display as hexadecimal (base 16). If you prefer to see
the
prefix printed, then you use the stream manipulator std::showbase (more details
will be
given in Chapter 8, Using the Standard Library Containers).
C++ defines some literals. For bool, the logic type, there are true and false
constants,
where false is zero and true is 1. There is also the nullptr constant, again, zero,
which is
used as an invalid value for any pointer type.
Understanding Language Features
[ 56 ]
Defining constants
In some cases, you will want to provide constant values that can be used throughout
your
code. For example, you may decide to declare a constant for π. You should not allow
this
value to be changed because it will change the underlying logic in your code. This
means
that you should mark the variable as being constant. When you do this, the compiler
will
check the use of the variable and if it is used in code that changes the value of
the variable
the compiler will issue an error:
const double pi = 3.1415;
double radius = 5.0;
double circumference = 2 * pi * radius;
In this case the symbol pi is declared as being constant, so it cannot change. If
you
subsequently decide to change the constant, the compiler will issue an error:
// add more precision, generates error C3892
pi += 0.00009265359;
Once you have declared a constant, you can be assured that the compiler will make
sure it
remains so. You can assign a constant with an expression as follows:
#include <cmath>
const double sqrtOf2 = std::sqrt(2);
In this code, a global constant called sqrtOf2 is declared and assigned with a
value using
the std::sqrt function. Since this constant is declared outside a function, it is
global to the
file and can be used throughout the file.
In the last chapter, you learned that one way to declare a constant is to use
#define
symbols. The problem with this approach is that the preprocessor does a simple
replacement. With constants declared with const, the C++ compiler will perform type
checking to ensure that the constant is being used appropriately.
You can also use const to declare a constant that will be used as a constant
expression. For
example, you can declare an array using the square bracket syntax (more details
will be
given in Chapter 4, Working with Memory, Arrays, and Pointers):
int values[5];
Understanding Language Features
[ 57 ]
This declares an array of five integers on the stack and these items are accessed
through the
values array variable. The 5 here is a constant expression. When you declare an
array on
the stack, you have to provide the compiler with a constant expression so it knows
how
much memory to allocate and this means the size of the array must be known at
compile
time. (You can allocate an array with a size known only at runtime, but this
requires
dynamic memory allocation, explained in Chapter 4, Working with Memory, Arrays, and
Pointers.) In C++, you can declare a constant to do the following:
const int size = 5;
int values[size];
Elsewhere in your code, when you access the values array, you can use the size
constant
to make sure that you do not access items past the end of the array. Since the size
variable
is declared in just one place, if you need to change the size of the array at a
later stage, you
have just one place to make this change.
The const keyword can also be used on pointers and references (see Chapter 4,
Working
with Memory, Arrays, and Pointers) and on objects (see Chapter 6, Classes); often,
you'll see it
used on parameters to functions (see Chapter 5, Using Functions). This is used to
get the
compiler to help ensure that pointers, references, and objects are used
appropriately, as you
intended.
Using constant expressions
C++11 introduces a keyword called constexpr. This is applied to an expression, and
indicates that the expression should be evaluated at compile type rather than at
runtime:
constexpr double pi = 3.1415;
constexpr double twopi = 2 * pi;
This is similar to initializing a constant declared with the const keyword.
However,
the constexpr keyword can also be applied to functions that return a value that can
be
evaluated at compile time, and so this allows the compiler to optimize the code:
constexpr int triang(int i)
{
return (i == 0) ? 0 : triang(i - 1) + i;
}
Understanding Language Features
[ 58 ]
In this example, the function triang calculates triangular numbers recursively. The
code
uses the conditional operator. In the parentheses, the function parameter is tested
to see if it
is zero, and if so the function returns zero, in effect ending the recursion and
returning the
function to the original caller. If the parameter is not zero, then the return
value is the sum
of the parameter and the return value of triang called with the parameter is
decremented.
This function, when called with a literal in your code, can be evaluated at compile
time. The
constexpr is an indication to the compiler to check the usage of the function to
see if it can
determine the parameter at compile time. If this is the case, the compiler can
evaluate the
return value and produce code more efficiently than by calling the function at
runtime. If
the compiler cannot determine the parameter at compile-time, the function will be
called as
normal. A function marked with the constexpr keyword must only have one expression
(hence the use of the conditional operator ?: in the triang function).
Using enumerations
A final way to provide constants is to use an enum variable. In effect, an enum is
a group of
named constants, which means that you can use an enum as a parameter to a function.
For
example:
enum suits {clubs, diamonds, hearts, spades};
This defines an enumeration called suits, with named values for the suits in a deck
of
cards. An enumeration is an integer type and by default the compiler will assume an
int,
but you can change this by specifying the integer type in the declaration. Since
there are just
four possible values for card suits, it is a waste of memory to use int (usually 4
bytes) and
instead we can use char (a single byte):
enum suits : char {clubs, diamonds, hearts, spades};
When you use an enumerated value, you can use just the name; however, it is usual
to
scope it with the name of the enumeration, making the code more readable:
suits card1 = diamonds;
suits card2 = suits::diamonds;
Both forms are allowed, but the latter makes it more explicit that the value is
taken from an
enumeration. To force developers to specify the scope, you can apply the keyword
class:
enum class suits : char {clubs, diamonds, hearts, spades};
Understanding Language Features
[ 59 ]
With this definition and the preceding code, the line declaring card2 will compile,
but the
line declaring card1 will not. With a scoped enum, the compiler treats the
enumeration as a
new type and has no inbuilt conversion from your new type to an integer variable.
For
example:
suits card = suits::diamonds;
char c = card + 10; // errors C2784 and C2676
The enum type is based on char but when you define the suits variable as being
scoped
(with class) the second line will not compile. If the enumeration is defined as not
being
scoped (without class) then there is an inbuilt conversion between the enumerated
value
and char.
By default, the compiler will give the first enumerator a value of 0 and then
increment the
value for the subsequent enumerators. Thus suits::diamonds will have a value of 1
because it is the second value in suits. You can assign values yourself:
enum ports {ftp=21, ssh, telnet, smtp=25, http=80};
In this case, ports::ftp has a value of 21, ports::ssh has a value of 22 (21
incremented),
ports::telnet is 22, ports::smtp is 25, and ports::http is 80.
Often the point of enumerations is to provide named symbols within your
code and their values are unimportant. Does it matter what value is
assigned to suits::hearts? The intention is usually to ensure that it is
different from the other values. In other cases, the values are important
because they are a way to provide values to other functions.
Enumerations are useful in a switch statement (see later) because the named value
makes
it clearer than using just an integer. You can also use an enumeration as a
parameter to a
function and hence restrict the values passed via that parameter:
void stack(suits card)
{
// we know that card is only one of four values
}
Declaring pointers
Since we are covering the use of variables, it is worth explaining the syntax used
to define
pointers and arrays because there are some potential pitfalls. Chapter 4, Working
with
Memory, Arrays, and Pointers, covers this in more detail, so we will just introduce
the syntax
so that you are familiar with it.
Understanding Language Features
[ 60 ]
In C++, you will access memory using a typed pointer. The type indicates the type
of the
data that is held in the memory that is pointed to. So, if the pointer is an (4
byte) integer
pointer, it will point to four bytes that can be used as an integer. If the integer
pointer

You might also like