Chapter 3. Variables and Operators

After completing this chapter, you will be able to:

  • Declare (create) variables.

  • Use the built-in C++ data types.

  • Use the Microsoft .NET Framework String class.

  • Assign values to a variable.

  • Create expressions by using the C++ operators.

  • Cast (change) the type of a variable.

In Chapter 2, you looked at the advantages of object-oriented programming and developed a simple application to illustrate the creation and use of classes.

In this chapter, you’ll take a closer look at how to create and use variables, the fundamental data types of C++, how to access and use classes from the .NET Framework, and how to create expressions by using C++ operators.

What is a variable?

Variables are locations in memory where data can be temporarily stored for use by the application. They have a name, a type, and a value. The value of the variable can be changed during the execution of the application; hence, the name variable. Before you can use a variable, you must declare it: you must specify its type, and you must give it a name. The type of a variable defines the allowable range of values that the variable can hold and the operations that you can perform on it.

The fundamental data types

C++ has a built-in set of data types, as outlined in the following table.

Type

Description

Comments

bool

A Boolean type that can contain the values true or false

 

char,__int8

A single-byte integral type, often used to hold ASCII values

Values can range from –128 to +127.

short,__int16

An integral type; stores whole numbers

Values can range from –32,768 to +32,767. An unsigned short can range from 0 to 65,535.

int,__int32

An integral type; stores whole numbers

Values can range from –2,147,483,648 to 2,147,483,647. An unsigned int can range from 0 to 4,294,967,295.

long

An integral type like int, except on many compilers, it’s twice the size

In Microsoft Visual C++, the long is the same size as the int. Therefore, it can only store the same range of values.

long long,__int64

An integral type

Values can range from –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

float

Stores floating-point numbers; for example, 3.7

In Visual C++, the float stores up to seven decimal places. The range of values is 3.4E+/-38.

double

Stores floating-point numbers like the float but with greater precision and more accuracy

The double can store up to 15 decimal places. The range of values is 1.7E+/-308

wchar_t

A wide character or multibyte character type

 

In some cases you’ll see that there is a C++ name (for example, int) and a Microsoft-specific equivalent (__int32). The two names are equivalent, and it is most common to use the C++ name.

From these built-in types, you can construct other types, as you’ll see in this chapter:

  • Handle types; for example, int^

  • Array types; for example, int[]

  • Reference types; for example, double%

Or, you can construct user-defined types by creating data structures and classes. Classes were introduced in Chapter 2 and are elaborated upon in Chapter 6. Data structures (structs) are covered in Chapter 9.

Declaring a variable

As I mentioned earlier, you must declare variables before you can use them. A simple declaration consists of a type, followed by one or more variable names separated by commas and terminated by a semicolon, as shown in the following example:

int primeNumber;
double x, y, z;

You can give each variable a qualifier before the type (for example, unsigned). You can also place an initializer after the variable name to give it an initial value (for example, int i = 0). The qualifier and the initializer are optional and are not required to appear in the declaration, but the base type and variable name must be present. The declaration is terminated by a semicolon.

[qualifier] type name [initializer];
unsigned int i;      // An unsigned integer variable i, note the
                     // qualifier limiting the variable to
                     // positive numbers.
long salary = 0;     // A long variable initialized to zero.
double y;            // A double variable without qualifier or
                     // initializer.

When you declare a variable, the compiler does the following

  • Allocates enough memory to store the variable of that type and to associate the name of the variable with that memory location.

  • Reserves the name of the variable to prevent it from being used by other variables within the same scope.

    Note

    Scope refers to that part of the code for which a variable is visible—in other words, where it can be used. The concept of scope is explained more in Chapter 4.

  • Ensures that the variable is used in a way consistent with its type. For example, if you have declared a variable as a char, you can’t store the value 3.7 in it.

Variable naming

A C++ variable name can be any combination of letters, numbers, and underscores, as long as the first character of the variable name is a letter or an underscore. Although C++ does not place any restrictions on your choice of variable names, they should be meaningful, and you should be consistent in your naming conventions to increase the readability of your code. C++ is case-sensitive. This means that myvariable and myVariable are two separate variables. However, it’s not a good idea to differentiate variables solely on the basis of case; doing so could lead to confusion. It would be easy to type a letter in the wrong case and end up using a completely wrong variable!

Note

As is mentioned in Chapter 1, it’s not a good idea to create identifiers that begin with two underscores or an underscore followed by a capital letter (for example, _A). Microsoft uses this naming convention to specify macros and Microsoft-specific keywords, so starting your variables with these combinations could lead to name conflicts.

Declaring multiple variables

You can declare several variables of the same type in the same statement simply by separating them with commas, as demonstrated in the following:

int x = 10, y, z = 11;

This statement creates three integers called x, y, and z. The first integer is initialized to 10 and the third to 11, whereas the second is not initialized.

Assigning values to variables

You assign a value to a variable by using the assignment operator = (the equal sign). The value on the right side of the operator is stored in the variable, which is on the left side. When assigning a value to a variable, the value must belong to the same type as the variable, it must be a type for which C++ will perform an assignment conversion (such as between float and integral types), or it must be explicitly converted (cast) to the correct type.

Handles and pointers

In standard C++, a pointer is a variable that holds the memory address of another variable or function, which means that you can use a pointer to refer indirectly to a variable.

In C++/CLI, however, the runtime is managing memory on your behalf, and it reserves the right to move things around to maximize the available free memory. This means that an object might not stay at the same address throughout its lifetime; thus, the address stored in your pointer might become out of date, leading to problems if you try to use it.

For this reason pointers, in the traditional C++ sense, are not used in C++/CLI. Instead, you use handles (also known as tracking handles), which also contain the address of a variable but which will be updated by the runtime if it has to move the variable around.

Although a handle contains an address and therefore can store a memory address of any data type, handle variables are declared to be data-type specific. A handle to a Person object can’t store the address of an Account. A handle variable is declared in the same way as the data-type variable, but the handle operator ^ (caret character) is prepended to the variable name:

Person ^pp;           // handle to a Person
Account ^ac;          // handle to an Account

Note

It is in fact possible to use pointers in some circumstances in C++/CLI, but that is beyond the scope for this introductory discussion.

You typically create an object dynamically and obtain a handle to it by using the gcnew operator, as illustrated here:

Person ^pp = gcnew Person("Fred");

This code instructs the runtime to create a new Person object, passing in the string “Fred” as initialization data, and return a handle to the object it has created.

When you access a member of an object through a handle, you use the pointer operator (->), which is discussed in more detail in the following chapters.

Arrays

An array is a collection of data-storage locations, each of which holds the same type of data, such as all integers or all doubles. Arrays are very useful when you want to represent a collection of values (such as the number of days in each month or the names of company employees) and you know how many you need to store.

Unlike classic C++, arrays in C++/CLI are objects that know how much data they are managing. This makes them safer than traditional C++ arrays because any attempt to read or write past the end of the array results in a run-time error, but does not corrupt memory.

Each storage location is called an element of the array. Elements of the array are accessed by an index, which starts at zero and continues up to one less than the array bound. Why not start the index from one? This is to preserve compatibility with other C-type languages, which all start array indexing from zero.

To declare an array, you need to specify the type of item that you are going to store. You create array objects dynamically by using the gcnew operator.

array<int> ^arr = gcnew array<int>(10);   // Declare an array of ten integers.
int x;
arr[0] = 23;     // The first element in the array starts at offset 0
arr[9] = 21;     // The last element in the array starts at offset 9
x = arr[0];      // Use an element from the array

Constants

Like variables, constants are named data-storage locations. However, unlike a variable, the value of a constant can’t be changed after it has been declared. It has to be initialized when it’s created and can’t be assigned a new value later. C++ has two types of constants: literal and symbolic.

A literal constant is simply a value typed into the application. The statements in the following code assign the literals 40 and Dog to the respective variables noOfEmployees and name:

noOfEmployees = 40;
name = "Dog";

A symbolic constant is a constant that is represented by a name. You define it in exactly the same way as a variable, but the qualifier must start with the keyword const and the variable must be initialized. After declaration, you can use the constant name anywhere that you can use a variable of that type, as shown in the following:

const unsigned long noOfFullTimeEmployees = 49;
const unsigned long noOfPartTimeEmployees = 234;
unsigned long noOfEmployees;
noOfEmployees = noOfFullTimeEmployees + noOfPartTimeEmployees;

There are a couple of advantages to using symbolic constants rather than literal constants:

  • The symbolic names make the application more readable. The symbolic constant noOfFull TimeEmployees is more meaningful than the literal constant 49.

  • It’s easier to change a single symbolic constant declaration than to find and replace all occurrences of a literal in a application.

However, using symbolic constants instead of literals can be taken too far. It is not necessary to replace all literals with constants. There are some constants that are intuitively obvious to everyone and that are not going to change; for example, the number of days in a week or months in a year. These values can be left as literals without reducing the readability or maintainability of the code.

Typedefs

A typedef is a user-defined synonym for an existing type. To create a synonym for a type, you use the keyword typedef followed by the name of the type and the new name you are defining. Because typedef is a C++ statement, you also need a closing semicolon:

typedef unsigned int positiveNumber;

This typedef declares positiveNumber to be a synonym of unsigned int and can be used in a declaration instead of the actual type name:

positiveNumber one, two;

The .NET Framework String class

The String class is not a built-in data type like int or long; it is a part of the .NET Framework. Because String isn’t a built-in type, you must include some files in your project before the compiler will let you use it. Code that wants to use the String class needs to include the following line at the top of its source code file:

using namespace System;

This line makes it easier to use certain .NET classes. Because String is in the System namespace, its full name is System::String, but a using namespace statement such as this makes it possible for you to use the name without qualification. This will be explained in more detail later on.

The String class contains a large number of methods to simplify manipulating strings, such as Insert and Replace.

Note

After you initialize a String object, it is immutable: It can’t be changed after it is created. The member functions of the String class that appear to alter strings, such as Insert and Replace, actually return a new String object, which contains the modified string. If you need to make repeated changes to a string, you should use the StringBuilder class, adding a using namespace statement for the System::Text namespace to simplify access.

Operators and expressions

Expressions are built by using operators that work with data—the operands—to give a result. Look at this example:

remuneration = salary + bonus;

Here the addition operator + (plus sign) is used to add the operands salary and bonus, and the assignment operator = (equal sign) is used to store the total in the remuneration variable.

Assignment operators

You use an assignment expression to assign a value to a variable. All expressions return a value when evaluated, and the value of the assignment expression becomes the new value of the object on the left side. This functionality makes it possible to assign the same value to a group of variables.

noOfMammals = noOfDogs = noOfCats = 0;

In this example, all three variables—noOfMammals, noOfDogs, and noOfCats—are set to 0.

Arithmetic operators

C++ has 12 arithmetic operators, 5 of which operate like the standard mathematical operators: the addition operator + (the plus sign), the subtraction operator – (the minus sign), the multiplication operator * (the asterisk), the division operator / (the slash), and the modulus operator % (the percent sign), which returns the remainder after division.

result = 4 + 2 - 3;  // result = 3
result = 4 * 5;      // result = 20
remainder = 7 % 3;   // remainder = 1

In addition, there are a number of arithmetic assignment operators, each of which consists of the operator and the = (equal sign): so the addition assignment operator += is a plus sign with an equal sign, and we also have –=, *=, /=, %=. These operators are shorthand forms that combine the corresponding mathematical operation with the assignment operation. So, the following two statements are identical:

a = a + 5;
a += 5;

The addition assignment operator is a shortcut operators; thus, there is no difference between the two statements. In both statements, an addition is performed, followed by an assignment. The second form is just a shorter way of expressing a frequently used operation.

The increment and decrement operators are similar shorthand operators, but these operators only add or subtract 1 from the value of the variable.

a++; // Adds 1 to the value of the variable a
a--; // Subtracts 1 from the value of the variable a

There are two forms of the increment and decrement operators: the prefix form ++a or – –a, and the postfix forms a++ or a– –. Although both forms add or subtract 1, in the prefix form, the mathematical operation is performed before the variable is used in the expression; in the postfix form, the variable is incremented or decremented after the variable has been used in the expression.

int a, b, c;
a = b = c = 0;
b = ++a;  // a = 1, b = 1
c = a++;  // c = 1, a = 2

In this code fragment, the final values of the variables are a = 2, b = 1, and c = 1. The prefix increment operator expression added 1 to the value of a before assigning the value of the variable a to the variable b. The postfix increment operator expression assigned the value of the variable a to the variable c and then incremented the value of the variable a by 1.

Relational and logical operators

Relational operators are used to compare two values or expressions, returning a value of true or false. C++ has six relational operators, as shown in the following code:

a > b    // returns true if a is greater than b.
a >= b   // returns true if a is greater than or equal to b.
a < b    // returns true if a is less than b.
a <= b   // returns true if a is less than or equal to b.
a == b   // returns true if a is equal to b.
a != b   // returns true if a is not equal to b.

A logical operator is used to relate two relational expressions. C++ has three logical operators: the AND operator && (two ampersands), the OR operator || (two pipes), and the NOT operator ! (an exclamation point). The AND operator relates two expressions, both of which must be true for the operator to return a true value. The OR operator returns true if either of the two expressions evaluates to true.

a && b               // returns true if both a and b are true
(a > b) && (a < c)   // returns true if a is greater than b and a
                     // is less than c
a || b               // returns true if either a or b are true
(a > b) || (a < c)   // returns true if either a is greater than b
                     // or a is less than c

The evaluation of a relational expression stops as soon as the logical value of the whole expression is determined, a feature known as short-circuit evaluation. For example, the expression expr1 && expr2 is true only if both expr1 and expr2 are true. If expr1 is false, the final value of the expression must be false, and therefore, expr2 is not evaluated.

The NOT operator returns the negation of the Boolean value of its operand:

!a    // returns false if a is true
      // returns true if a is false

These operators are most often used in decision or loop structures, which are discussed in Chapter 5.

Bitwise operators

C++/CLI has six bitwise operators: the AND operator & (an ampersand), the OR operator | (a vertical bar), the exclusive OR operator ^ (a caret), the complement operator ~ (a tilde), the right-shift operator >> (two right angle brackets), and the left-shift operator << (two left angle brackets). These operators work on the individual bits of the byte and can only be applied to integral operands—the types char, short, int, and long. The bitwise AND operator compares the bits of two operands; if the bit in the same position for each operand is 1, the resulting bit is 1; if, however, either bit is 0 the resulting bit is set to 0. This operator is often used to mask off bits.

The bitwise OR operator compares the bits of two operands. If either bit is 1, the corresponding bit of the result is 1, and if both bits are 0, the corresponding bit of the result is set to 0. The bitwise OR operator is often used to turn on bits, flags, or options.

The exclusive OR operator sets the result bit to 1 only if one of the operands has the corresponding bit set to 1. If the corresponding bit of both operands is 1 or 0, the bit is set to 0.

The complement operator reverses the bit setting of the operand. If the bit is 1, it is set to 0; if the bit is 0, it is set to 1.

The left-shift operator moves the bit pattern of its left operand to the left by the number of bits specified by its right operand. The bits vacated by the left shift are filled with zeros. The right-shift operator moves the bit pattern of its right operand to the right by the number of bits specified by its right operand. If the variable is an unsigned data type, the vacated bits will be filled with zeros; if the variable is signed, the vacated bits will be filled with the sign bit.

int a;
a = 5;
a = a << 2;   // The bits of a will be shifted two bits to the left
              // and the value of 20 assigned to a.
a = 5;
a = a >> 2;   // The bits of a will be shifted two bits to the
              // right and the value of 1 assigned to a.

The ternary operator

The ternary operator ?: (a question mark and a colon) acts like an inline if statement. (See Chapter 5 for more information on if statements.) The expression to the left of the question mark is evaluated; if it is true, the value or expression between the question mark and the colon will be returned. If it is false, the value or expression after the colon will be returned.

int a;
bool b;
b = true;
a = b ? 1 : 2;   // b is true, so a is assigned 1.
b = false;
a = b ? 1 : 2;   // b is false, so a is assigned 2.

Type casting

C++/CLI supports the C-style cast operator, whereby the type to which you want to convert the expression is placed in parentheses in front of the expression; for example, (float) 7. It also supports five C++ cast operators:

  • static_cast<>

  • const_cast<>

  • dynamic_cast<>

  • safe_cast<>

  • reinterpret_cast<>

The static_cast<> operator changes the data type of the variable, with the type to which you want to cast being placed in the angle brackets. For example, if an expression needs to convert an int to a double, the number should be cast by using the static_cast<double> operator. Here’s an example:

int a = 10;

double b;

b = (int) a;                 // old C-style cast

b = static_cast<double>(a);  // C++ static cast

You use the dynamic_cast<> operator to cast objects down or across the inheritance hierarchy. The const_cast<> operator works with pointers, and references can be used to add or remove the const qualification of the variable. The safe_cast<> operator is an extension added to C++/CLI; it performs the same function as dynamic_cast<> but throws an exception if the cast fails. Using the reinterpret_cast<> operator, you can convert any pointer to a pointer of another type. This particular operator is not used that often in application code.

Operator precedence and associativity

There are two ways by which the expression 2 + 3 * 4 could be evaluated: It could be evaluated as (2 + 3) * 4, yielding a value of 20, or it could be evaluated as 2 + (3 * 4), yielding a value of 14.

The rules of operator precedence specify an unambiguous evaluation of expressions. Operators higher in the hierarchy are given precedence over operators lower in the hierarchy. Because the * operator is higher than the + operator, the second interpretation of the above expression, 2 + (3 * 4), would be evaluated. For situations in which two operators are at the same level in the hierarchy, the order of evaluation proceeds from left to right. Hence, 2 * 3 / 2 * 3 would be evaluated as ((2 * 3) / 2) * 3, giving a value of 9. Parentheses can be used to group operators and override the precedence hierarchy. For example, (2 * 3) / (2 * 3) results in a value of 1. Many people use parentheses even when they are not strictly required, simply to clarify their intentions. The following table shows the hierarchy of precedence from highest to lowest. Operators in the same row of the table share the same level of precedence.

Operator

Name

:: [] ()

Scope resolution, subscripting, function calls

static_cast<>

const_cast<>

dynamic_cast<>

reinterpret_cast<>

safe_cast<>

Casting operators

sizeof ++ –– ^ ! – + & *

sizeof(), increment, decrement, complement, not, unary minus, unary plus, address of, dereference

* / %

Arithmetic operators

+ -

 

<< >>

Bit-shifting operators

< <= => >

Logical inequality operators

= = !=

 

&

Bitwise AND

^

Exclusive OR

|

Bitwise OR

&&

Logical AND

||

Logical OR

?:

Ternary operator

= += -+ *= /= %= <<= >>= &= != ^=

 

,

Comma

Quick reference

To

Do this

Declare a variable.

Specify the type, followed by spaces and then the variable name, followed by a semicolon. For example:

int number1;
long longNumber1;

Assign values to a variable.

Use the assignment operator =.

Group homogenous data together.

Use an array.

Prevent data from being changed.

Make the variable a constant. For example:

const int x = 10;

Restrict the values a variable can accept to a small set.

Declare an enumerated constant, and declare the variable to be of that type.

Access a String class.

Use the .NET String class.

Convert one data type to another.

Use the static_cast<> operator.

Override default operator precedence, or make the code more readable.

Use parentheses to group operators.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.59.145