原创  Chapter 2 Variables and Basic Types 收藏

Chapter 2 Variables and Basic Types

2.3 Variable

  • key Concept: Strong Static Typing

C++ is a statically typed language, which means that types are checked at compile time. In C++, whether an operation is legal or not is checked at compile time. A consequence of static checking is that the type of every entity used in our programs must be known to the compiler.

2.3.1 what is a variable?

A variable provides us with named storage that our programs can manipulate.

 

  1. Each variable in C++ has following items:
    • The Specific type: determine the size and layout of the variable’s memory
    • The range of values: that can be stored in the memory.
    • The set of operations: that can be applied to the variable

 

  1. lvalue and rvalue
    • lvalue: an expression that is an lvalue may appear as either the left-hand or right-hand side of an assignment.
    • rvlaue: An expression that is an rvalue may appear on the right- but not left-hand side of an assignment.

2.3.2 The Name of a Variable

·         Identifier: The Name of a variable.

·         In C++, identifier are case-sensitive

·         It must begin with either a latter or an underscore.

·         In addition to the keywords, the standard also reserves a set of identifiers for use

in the library. Identifiers cannot contain two consecutive underscores, nor can an identifier begin with an underscore followed immediately by an upper-case letter.

 

  1. C++ keywords

asm

do

if

return

try

auto

double

inline

short

typedef

bool

dynamic_cast

int

signed

typeid

break

else

long

sizeof

typename

case

enum

mutable

static

union

catch

explicit

namespace

static_cast

unsigned

char

export

new

struct

using

class

extern

operator

switch

virtual

const

false

private

template

void

const_cast

float

protected

this

volatile

continue

for

public

throw

wchar_t

default

friend

register

true

while

delete

goto

reinterpret_cast

 

 

 

  1. C++ operator alternative names

 

and

bitand

compl

not_eq

or_eq

xor_eq

and_eq

bitor

not

or

xor

 

 

 

2.3.3 Defining Objects

Each definition starts with a type specifier, followed by a comma-separated list of one or more names. A semicolon terminates the definition.

Multiple variables may be defined in a single statement.

  1. Variable Initialization: a definition specifies a variable’s type and identifier. In addition, it may also provide an initial value. Tow forms of variables initialization: copy-initialization and direct-initialization.
    1. direct-initialization

               int ival(1024);     // direct-initialization

 

    1. copy-initialization

int ival = 1024;    // copy-initialization

  1. initialization and assignment are different operations in C++
  2. the different between initialization and assignment

Initialization happens when a variable is created and gives that variable its initial value. Assignment involves obliterating an object's current value and replacing that value with a new one.

  1. There are subtle differences between copy- and direct-initialization when initializing objects of a class type. We won't completely explain these differences until Chapter 13. For now, it's worth knowing that the direct syntax is more flexible and can be slightly more efficient.

 

2.3.4. Variable Initialization Rules

When we define a variable without an initializer, the system sometimes initializes the variable for us. What value, if any, is supplied depends on the type of the variable and may depend on where it is defined.

  • Initialization of Variables of Built-in type

o        Global variables are initialized to zero

o        Local variables are unitialized

o        Using an uninitialized variable for anything other than as the left-hand operand of an assignment is undefined

o        We recommend that every object of built-in type be initialized. It is not always necessary to initialize such variables, but it is easier and safer to do so until you can be certain it is safe to omit an initializer.

  • Initialization of Variables of Class type

o        Classes control object initialization by defining one or more constructors, even the system special constructor, known as the default constructor.

o        The default constructor is used regardless of where a variable is defined.

o        Most classes provide a default constructor. If the class has a default constructor, then we can define variables of that class without explicitly initializing them.

o        Some class types do not have a default constructor. For these types, every definition must provide explicit initializer(s). It is not possible to define variables of such types without giving an initial value.

 

 

2.3.5 Declarations and Definitions

  • Definition

A definition of a variable allocates storage for the variable and may also specify an initial value for the variable. There must be one and only one definition of a variable in a program.

  • declaration

a declaration makes known the type and name of the variable to the program. A definition is also a declaration.

When we define a variable, we declare its name and type. We can declare a name without defining it by using the extern keyword. A declaration that is not also a definition consists of the object's name and its type preceded by the keyword extern:

  • examples
        extern int i;   // declares but does not define i
        int i;          //  declares and defines i
        extern double pi = 3.1416; // definition
 

Despite the use of extern, this statement defines pi. Storage is allocated and initialized. An extern declaration may include an initializer only if it appears outside a function.

 

Any variable that is used in more than one file requires declarations that are separate from the variable's definition. In such cases, one file will contain the definition for the variable. Other files that use that same variable will contain declarations forbut not a definition ofthat same variable.

2.3.6 Scope of a Name

A scope is a region of the program. A name can refer to different entities in different scopes.

  • Global Scope

Names defined outside any function have global scope; they are accessible from anywhere in the program.

  • Local Scope

Names defined in any function. It is accessible throughout the function but not outside of it.

  • Statement Scope

It can be used in that statement but not elsewhere in main.

  • Class Scope
  • Namespace Scope

 

Scopes in C++ Nest

  • Names defined in the global scope can be used in a local scope;
  • Global names and those defined local to a function can be used inside a statement scope.
  • Names can also be redefined in an inner scope
  • The variable defined in the inner scope will hide the variable defined in outer scope, if they have the same name
  • It recommend that using different name between global variable(outer variable) and local variable(inner variable).

Examples

#include <iostream>

#include <string>

/******************************************************************************************

*   Program for illustration purposes only:

  *  It is bad style for a function to use a global variable and then

 *  define a local variable with the same name

 ******************************************************************************************/

 

std::string s1 = "hello";  // s1 has global scope

 

int main()

{

            std::string s2 = "world"; // s2 has local scope

           

            // uses global s1; prints "hello world"

            std::cout << s1 << " " << s2 << std::endl;

           

           

            int s1 = 42; // s1 is local and hides global s1

           

            // uses local s1;prints "42 world"

            std::cout << s1 << " " << s2 << std::endl;

           

            return 0;

}

 

/******************************************************************************************

*    the output of the test case

*

*

 ******************************************************************************************/

#if 0

hello world

42 world

#endif

 

 

 

Best Priactise

It is usually a good idea to define an object near the point at which the object is first used.

  • Defining an object where the object is first used improves readability
  • Moreover, it is often easier to give the variable a useful initial value when the variable is defined close to where it is first used.

 

 

2.4 Const

The const type qualifier provides a solution: it transforms an object into a constant.

 

const double pi = 3.14;

 

  • the const variable is still an lvalue, but the lvalue is unmodifiable.
        bufSize = 0; // error: attempt to write to const object
 
  • We must initialize the const variable when it is defined, because we cann’t subsequently change the value of an object declared to be const.
        const std::string hi = "hello!"; // ok: initialized
        const int i, j = 0;  // error: i is uninitialized const

 

  • const Objects Are Local to a File By Default

Unlike other variables, unless otherwise specified, const variables declared at global scope are local to the file in which the object is defined. The variable exists in that file only and cannot be accessed by other files.

  • We can make a const object accessible throughout the program by specifying that it is extern:

 

// file_1.cc
      // defines and initializes a const that is accessible to other files
      extern const int bufSize = fcn();
      // file_2.cc
      extern const int bufSize; // uses bufSize from file_1
      // uses bufSize defined in file_1
      for (int index = 0; index != bufSize; ++index)

 

 

 

Note:

Nonconst variables are extern by default. To make a const variable accessible to other files we must explicitly specify that it is extern.

 

 

2.5 Reference

A reference serves as an alternative name for an object. In real-world programs, reference are primarily used as formal parameter to functions.

 

  • Defining Multiple References

We can define multiple references in a single type definition. Each identifier that is a reference must be preceded by the & symbol. The usage of defining multiple references is similar to defining multiple pointers.

 

        int i = 1024, i2 = 2048;
        int &r = i, r2 = i2;      // r is a reference, r2 is an int
        int i3 = 1024, &ri = i3;  // defines one object, and one refer
        int &r3 = i3, &r4 = i2;   // defines two references

 

  • A reference is an alias

Because a reference is just another name for the object to which it is bound, all operations on reference are actually operations on the underlying object to which the reference is bound. We can access a variable either through its actual name or through its alias.

 

Int ival = 1024;

Int &refVal = ival;

            refVal += 2; // the same to ival +=2;

 

  • you must initialize a reference when you define it; initialization is the only way to say to which object a reference refers
  • A reference must be initialized using an object of the same type as the reference.
  • When a reference is initialized, it remains bound to that object as long as the reference exists. There is no way to rebind a reference to a different object.

Note: const reference V.S non-const reference

  • A const reference is a reference that may refer to a const object.
  • A non-const reference is a reference that must refer to non-const object. It can’t refer to an const object
  • A const reference can be initialized to an object of a different type or to an rvalue, such as a literal constant.
  • A  none-const reference must refer to the same type as the reference. And it can’be initialized by an rvalue, literal constant.

 

        double dval = 3.14;
        const int &ri = dval;

the compiler transforms this code into something like this:

        int temp = dval;         // create temporary int from the double
        const int &ri = temp;   // bind ri to that temporary
 

 

 

 

Examples

#include <iostream>

using namespace std;

 

 

 

 

/*********************************************************************************************/

/* reference.cc: the major purpose is to describe the usage of reference, including const-reference and

*           non-const reference.

*

*/

/********************************************************************************************/

 

 

 

/*********************************************************************************************/

/* referenceBasicUsageTester: illustrate the basice usage of reference

*           1. reference must be initialized.

*           2. operating the reference is the same to operate the actual object.

*           3. reference don't rebind to another object. the initialization is the only way to say which object it refers to.

*/

/********************************************************************************************/

void referenceBasicUsageTester()

{

            cout << "##########  referenceBasicUsageTester()  ############" << endl;

           

            int ival = 1024;

            int ival2 = 2048;

           

            int &refVal = ival; // ok: refVal refers to ival

            //int &refVal2;       // error: a reference must be initialized

            //int &refVal3 = 10;  // error: initializer must be an object,

            int &refVal2 = refVal;

            refVal2 +=2;

           

            /*can't rebind to another variable after the reference is initialized*/

            //&refVal = ival2;          //error: ISO C++ forbids cast to non-reference type used as lvalue

                                                            //error:  confused by earlier errors, bailing out

           

            cout << "ival = " << ival << endl;

            cout << "refVal = " << refVal << endl;

            cout << "refVal2 = " << refVal2 << endl;

}

 

 

/*********************************************************************************************/

/* constReferenceUsageTester: const reference have some special usage, which  differ to non-cosnt reference;

*           so the function try to tell the differences between them.

*           1. cosnt reference may refer to const object. in contrast, the non-const reference can't be initialized by

*                       const object.

*           2. const reference can be initialized by differnet type.

*           3. const reference can be initalized by rvlaue, such as literal cosntant, temporary variable .

*           4.

*/

/********************************************************************************************/

void constReferenceUsageTester()

{

            cout << "##########  constReferenceUsageTester()  ############" << endl;

           

            /*const object*/

            int iVal = 1024;

            const int iVal2 = 2048;

           

            int &ref = iVal; //ok

            //int &ref2 = iVal2;        //error: could not convert `iVal2' to `int&'

           

            const int &ref3 = iVal; //ok;

            const int &ref4 = iVal2; //ok;

           

            /**********************/

            //int &ref5 = 5;                         // error: could not convert `5'   to `int&'

            const int &ref6  = 10; //ok

           

            //int &ref7 = iVal2 + 10;                       // error: could not convert `2058'   to `int&'

            const int &ref8 = iVal2 + 10;

           

           

            /**********************/

            double dVal = 3.14;

            //int &dRef = dVal;       // error: could not convert `  dVal' to `int&'

            const int &dRef2 = dVal;

           

}

 

 

 

 

 

 

 

 

 

/*********************************************************************************************/

/* :

*

*/

/********************************************************************************************/

int main()

{

            cout << "the main to illustrate the usage of reference " << endl;

            referenceBasicUsageTester();

            constReferenceUsageTester();

            return 0;

}

 

 

 

 

Question?

What are the differences among the definitions in (a) and the assignments in (b)? Which, if any, are illegal?

     (a) int ival = 0;          (b) ival = ri;
         const int &ri = 0;         ri = ival;

 

2.6 Typedef

A typedef lets us define a synonym for a type. The identifier, or typedef name, does not introduce a new type but rather a synonym for the existing data type.

 

Typedefs are commonly used for one of three purposes:

  • To hide the implantation of a given type and emphasize instead the purpose for which the type is used.
  • To streamline complex type definitions, making them easier to understand.
  • To allow a single type to be used for more than one purpose while making the purpose clear each time the type is used.

Examples

#include <iostream>

using namespace std;

 

/*********************************************************************************************/

/* typedef.cc:  A typedef lets us define a synonym for a type, it doesn't introduce a new type. it is just aliase.

*

*/

/********************************************************************************************/

 

 

 

/*********************************************************************************************/

/* :

*

*/

/********************************************************************************************/

void fun1(int theI)

{

            cout << "void fun1(int theI)" << endl;

            cout << "theI = " << theI << endl;

}

 

void typedefBasicTester()

{

            /***********************************/

            typedef short INT16;

            typedef unsigned short UINT16;

           

            INT16 a = 2;

            UINT16 b = 3;

           

            /*using typedef to give struct and enum a synonym, in addition, it is convenient to define variable*/

            typedef struct StructA{

                        int i;

                        char str;

            } StructType;

           

           

            StructType struct1;

            StructType s2, s3;

           

            s2.i = 2;

            s3.i = 3;

           

            cout << "s2. i = " << s2.i << endl;

            cout << "s3. i = " << s3.i << endl;      

           

            /*illustarte the usage of function pointer*/      

            typedef void (*fun)(int);

            fun pf = fun1;

           

            pf(2);   

}

 

 

 

/*********************************************************************************************/

/* :

*

*/

/********************************************************************************************/

int main()

{

            cout << "the main to illustrate the usage of typedef name! " << endl;

           

            typedefBasicTester();

           

           

            return 0;

}

 

 

2.7 Enumerations

  • Enumerations provide an alternative method of not only defining but also grouping sets of integral constants.
  • Enumerators are const Values.
  • We may supply an initial value for one or more enumerators. The value used to initialize an enumerator must be a constant expression. A constant expression is an expression of integral type that the compiler can evaluate at compile time
  • By default, the first enumerator is assigned the value zero. Each subsequent enumerator is assigned a value one greater than the value of the enumerator that immediately precedes it.

Examples

 

#include <iostream>

using namespace std;

 

/*********************************************************************************************/

/* Enum.cc

*           Enumerations provide an alternative method of not only defining but also grouping sets of integral constants.

*

*/

/********************************************************************************************/

 

 

 

void enumTester()

{

            enum returnTypes {

                        RETURN_TYPE_UNDEF = -1,

                        RETURN_TYPE_SW_FAIL,

                        RETURN_TYPE_HW_FAIL,

                        RETURN_TYPE_NETWORK_FAIL,

                        RETURN_TYPE_SYSTEM_FAIL,

                        RETURN_TYPE_SUCCESS

            };

           

            enum fileTypes {

                                    FILE_TYPE_UNDEF = -1,

                                    FILE_TYPE_READ,

                                    FILE_TYPE_WRITE,

                                    FILE_TYPE_READ_WRITE

            };

           

            /*******************************/

            enum DefaultValue{

                        DEFAULT_VALUE_1, //the default value of the first enumrator is 0,

                        DEFAULT_VALUE_2,

            };

           

            cout << "DEFAULT_VALUE_1 = " << DEFAULT_VALUE_1 << endl;

            /*******************************/

            enum Points {            

                        point2d = 2,

                        point2w,    //one more than the value of the privious line;

                        point3d = 3,

                        point3w

            };

 

            cout << "point2w = " << point2w << endl; // output is 3;

            cout << "point3w = " << point3w << endl; // output is 4;

           

            /*******************************/

            returnTypes rt1 = RETURN_TYPE_UNDEF; //ok

           

            //returnTypes rt2 = 1; // error: invalid conversion from ` int' to `enumTester()::returnTypes'

           

            returnTypes rt3 = (returnTypes)1; // ok, explicitly const 1 to returnTypes;

           

            //returnTypes rt4 = (RETURN_TYPE_HW_FAIL)1; // error: syntax error before numeric constant

           

            //error: cannot convert `enumTester()::fileTypes' to `enumTester()::returnTypes' in initialization

            //returnTypes rt5 = FILE_TYPE_UNDEF;

           

}

 

/*********************************************************************************************/

/* :

*

*/

/********************************************************************************************/

int main()

{

            cout << "the main to illustrate the usage of typedef name! " << endl;

           

             enumTester();

           

           

            return 0;

}

 

 

 

 

2.8 Class Types

Each class defines an interface and implementation.

  • The interface consists of the operations that we expect code that uses the class to execute.
  • The implementation typically includes the data needed by the class. The implementation also includes any functions needed to define the class but that are not intended for general use.

Note: the differences between Class and Struct

The only difference between a class defined with the class keyword or the struct keyword is the default access level: By default, members in a struct are public; those in a class are private.

 

 

 

2.9 Head Files

 

 

Proper use of head files can provide two benefits:

  • All files are guaranteed to use the same declaration for a given entity;
  • If a declaration requires change, only the header needs to be updated;

 

When designing a header it is essential to remember the difference between definitions, which may only occur once, and declarations, which may occur multiple times

 

Beware: which should be included in the head files

  • Because headers are included in multiple source files, they should not contain definitions of variables or functions.
  • There are three exceptions to the above rule that headers should not contain definitions: classes, const objects whose value is known at compile time, and inline functions  covers inline functions) are all defined in headers
  • These entities are defined in headers because the compiler needs their definitions (not just declarations) to generate code. For example, to generate code that defines or uses objects of a class type, the compiler needs to know what data members make up that type. It also needs to know what operations can be performed on these objects. The class definition provides the needed information. That const objects are defined in a header may require a bit more explanation.

 

 

Generally speaking, a constant expression is an expression that the compiler can evaluate at compile-time

 

Avoiding Multiple Inclusions

A common way to make headers safe uses the preprocessor to define a header guard. The guard is used to avoid reprocessing the contents of a header file if the header has already been seen.

        
        #ifndef SALESITEM_H
        #define SALESITEM_H
 
        // Definition of Sales_itemclass and related functions goes here
 
        #endif
 

 

Best Practices

  • Headers should have guards, even if they aren't included by another header.
  • The conditional directive usually are written in all uppercase letters
  • If the head files are changed, all files including the head files should be recompiled.

 

Using head files

  • Standard header: The compiler will look for it in a predefined set of locations, which can be modified by setting a search path environment variable or through a command line option
  • User Header files: The search for nonsystem headers usually begins in the directory in which the source file is located

 

        #include <standard_header> // standard header
        #include "my_file.h"   // user header
 

发表于 @ 2007年10月30日 22:13:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:Sneaky food books: Hot and a hot topic | 新一篇:Installing Bochs/Unix_v6 on your 32-bit Linux machine

  • 发表评论
  • 评论内容:
  •  
Copyright © lengxingfei
Powered by CSDN Blog