Tải bản đầy đủ (.pdf) (20 trang)

C++ Primer Plus (P27) docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (474.84 KB, 20 trang )

float * p_fees; // = new float[20] not allowed here
int main()
{
p_fees = new float [20];

Compatibility Note
Memory allocated by new typically is freed when the
program terminates. However, this is not always true.
Under DOS, for example, in some circumstances a request
for a large block of memory can result in a block that is not
deleted automatically when the program terminates.
Namespaces
Names in C++ can refer to variables, functions, structures, enumerations, classes, and
class and structure members. When programming projects grow large, the potential for
name conflicts increases. When you use class libraries from more than one source, you
can get name conflicts. For example, two libraries might both define classes named List,
Tree, and Node, but in incompatible ways. You might want the List class from one library
and the Tree from the other, and each might expect its own version of Node. Such
conflicts are termed namespace problems.
The C++ Standard provides namespace facilities to provide greater control over the scope
of names. It has taken a while for compilers to incorporate namespaces, but, by now,
support has become common.
Traditional C++ Namespaces
Before looking at the new facilities, let's review the namespace properties that already exist
in C++ and introduce some terminology. This can help make the idea of namespaces seem
more familiar.
The first term is declarative region. A declarative region is a region in which declarations
can be made. For example, you can declare a global variable outside of any function. The
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
declarative region for that variable is the file in which it is declared. If you declare a variable
inside a function, its declarative region is the innermost block in which it is declared.


The second term is potential scope. The potential scope for a variable begins at its point
of declaration and extends to the end of its declarative region. So the potential scope is
more limited than the declarative region because you can't use a variable above the point it
is first defined.
A variable, however, might not be visible everywhere in its potential scope. For example, it
can be hidden by another variable of the same name declared in a nested declarative
region. For example, a local variable declared in a function (the declarative region is the
function) hides a global variable declared in the same file (the declarative region is the file).
The portion of the program that actually can see the variable is termed the scope, which is
the way we've been using the term all along. Figures 9.5 and 9.6 illustrate the terms
declarative region, potential scope, and scope.
Figure 9.5. Declarative regions.
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
Figure 9.6. Potential scope and scope.
C++'s rules about global and local variables define a kind of namespace hierarchy. Each
declarative region can declare names that are independent of names declared in other
declarative regions. A local variable declared in one function doesn't conflict with a local
variable declared in a second function.
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
New Namespace Features
What C++ now adds is the ability to create named namespaces by defining a new kind of
declarative region, one whose main purpose is to provide an area in which to declare
names. The names in one namespace don't conflict with the same names declared in other
namespaces, and there are mechanisms for letting other parts of a program use items
declared in a namespace. The following code, for example, uses the new keyword
namespace to create two namespaces, Jack and Jill.
namespace Jack {
double pail;
void fetch();
int pal;

struct Well { };
}
namespace Jill {
double bucket(double n) { }
double fetch;
int pal;
struct Hill { };
}
Namespaces can be located at the global level or inside other namespaces, but they
cannot be placed in a block. Thus, a name declared in a namespace has external linkage
by default (unless it refers to a constant).
In addition to user-defined namespaces, there is one more namespace, the global
namespace. This corresponds to the file-level declarative region, so what used to be
termed global variables are now described as being part of the global namespace.
The names in any one namespace can't conflict with names in another namespace. Thus,
the fetch in Jack can coexist with the fetch in Jill, and the Hill in Jill can coexist with an
external Hill. The rules governing declarations and definitions in a namespace are the
same as the rules for global declarations and definitions.
Namespaces are open, meaning you can add names to existing namespaces. For
example, the statement
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
namespace Jill {
char * goose(const char *);
}
adds the name goose to the existing list of names in Jill.
Similarly, the original Jack namespace provided a prototype for a fetch() function. You can
provide the code for the function later in the file (or in another file) by using the Jack
namespace again:
namespace Jack {
void fetch()

{

}
}
Of course, you need a way to access names in a given namespace. The simplest way is to
use ::, the scope resolution operator, to qualify a name with its namespace:
Jack::pail = 12.34; // use a variable
Jill::Hill mole; // create a type Hill structure
Jack::fetch(); // use a function
An unadorned name, such as pail, is termed the unqualified name, whereas a name with
the namespace, as in Jack::pail, is termed a qualified name.
Using-Declarations and Using-Directives
Having to qualify names every time they are used is not an appealing prospect, so C++
provides two mechanisms—the using-declaration and the using-directive—to simplify
using namespace names. The using-declaration lets you make particular identifiers
available, and the using-directive makes the entire namespace accessible.
The using-declaration consists of preceding a qualified name with the new keyword using:
using Jill::fetch; // a using-declaration
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
A using-declaration adds a particular name to the declarative region in which it occurs. For
example, a using-declaration of Jill::fetch in main() adds fetch to the declarative region
defined by main(). After making this declaration, you can use the name fetch instead of
Jill::fetch.
namespace Jill {
double bucket(double n) { }
double fetch;
struct Hill { };
}
char fetch;
int main()

{
using Jill::fetch; // put fetch into local namespace
double fetch; // Error! Already have a local fetch
cin >> fetch; // read a value into Jill::fetch
cin >> ::fetch; // read a value into global fetch

}
Because a using-declaration adds the name to the local declarative region, this example
precludes creating another local variable by the name of fetch. Also, like any other local
variable, fetch would override a global variable by the same name.
Placing a using-declaration at the external level adds the name to the global namespace:
void other();
namespace Jill {
double bucket(double n) { }
double fetch;
struct Hill { };
}
using Jill::fetch; // put fetch into global namespace
int main()
{
cin >> fetch; // read a value into Jill::fetch
other()
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.

}
void other()
{
cout << fetch; // display Jill::fetch

}

A using-declaration, then, makes a single name available. In contrast, the using-directive
makes all the names available. A using-directive consists of preceding a namespace name
with the keywords using namespace, and it makes all the names in the namespace
available without using the scope resolution operator:
using namespace Jack; // make all the names in Jack available
Placing a using-directive at the global level makes the namespace names available
globally. You've seen this in action many a time:
#include <iostream> // places names in namespace std
using namespace std; // make names available globally
Placing a using-directive in a particular function makes the names available just in that
function.
int vorn(int m)
{
using namespace jack; // make names available in vorn()

}
Using-Directive Versus Using-Declaration
Using a using-directive to import all the names wholesale is not the same as using multiple
using-declarations. It's more like the mass application of a scope resolution operator. When
you use a using-declaration, it is as if the name is declared at the location of the using-
declaration. If a particular name already is declared in a function, you can't import the same
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
name with a using-declaration. When you use a using-directive, however, name resolution
takes place as if you declared the names in the smallest declarative region containing both
the using-declaration and the namespace itself. For the following example, that would be
the global namespace. If you use a using-directive to import a name that already is
declared in a function, the local name will hide the namespace name, just as it would hide
a global variable of the same name. However, you still can use the scope resolution
operator:
namespace Jill {

double bucket(double n) { }
double fetch;
struct Hill { };
}
char fetch; // global namespace
int main()
{
using namespace Jill; // import all namespace names
Hill Thrill; // create a type Jill::Hill structure
double water = bucket(2); // use Jill::bucket();
double fetch; // not an error; hides Jill::fetch
cin >> fetch; // read a value into the local fetch
cin >> ::fetch; // read a value into global fetch
cin >> Jill::fetch; // read a value into Jill::fetch

}
int foom()
{
Hill top; // ERROR
Jill::Hill crest; // valid
}
Here, in main(), the name Jill::fetch is placed in the local namespace. It doesn't have local
scope, so it doesn't override the global fetch. But the locally declared fetch hides both
Jill::fetch and the global fetch. However, both of the last two fetch variables are available
if you use the scope resolution operator. You might want to compare this example to the
preceding one, which used a using-declaration.
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
One other point of note is that although a using-directive in a function treats the
namespace names as being declared outside the function, it doesn't make those names
available to other functions in the file. Hence in the preceding example, the foom() function

can't use the unqualified Hill identifier.
Remember
Suppose a namespace and a declarative region
both define the same name. If you attempt to
use a using-declaration to bring the namespace
name into the declarative region, the two
names conflict, and you get an error. If you use
a using-directive to bring the namespace name
into the declarative region, the local version of
the name hides the namespace version.
Generally speaking, the using-declaration is safer to use because it shows exactly what
names you are making available. And if the name conflicts with a local name, the compiler
lets you know. The using-directive adds all names, even ones you might not need. If a local
name conflicts, it overrides the namespace version, and you won't be warned. Also, the
open nature of namespaces means that the complete list of names in a namespace might
be spread over several locations, making it difficult to know exactly which names you are
adding.
What about the approach used for this book's examples?
#include <iostream>
using namespace std;
First, the iostream header file puts everything in the std namespace. Then, the next line
exports everything in that namespace into the global namespace. Thus, this approach
merely reproduces the pre-namespace era. The main rationale for this approach is
expediency. It's easy to do, and if your system doesn't have namespaces, you can replace
the preceding two lines with the original form:
#include <iostream.h>
However, the hope of namespace proponents is that you will be more selective and use
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
either the resolution operator or the using-declaration. That is, don't use the following:
using namespace std; // avoid as too indiscriminate

Instead, do this:
int x;
std::cin >> x;
std::cout << x << std::endl;
Or else do this:
using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;
You can use nested namespaces, as described next, to create a namespace holding the
using-declarations you commonly use.
More Namespace Features
You can nest namespace declarations:
namespace elements
{
namespace fire
{
int flame;

}
float water;
}
In this case, you refer to the flame variable as elements::fire::flame. Similarly, you can
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
make the inner names available with this using-directive:
using namespace elements::fire;
Also, you can use using-directives and using-declarations inside namespaces:
namespace myth

{
using Jill::fetch;
using namespace elements;
using std::cout;
using std::cin;
}
Suppose you want to access Jill::fetch. Because Jill::fetch is now part of the myth
namespace, where it can be called fetch, you can access it this way:
std::cin >> myth::fetch;
Of course, because it also is part of the Jill namespace, you still can call it Jill::fetch:
std::cout << Jill::fetch; // display value read into myth::fetch
Or you can do this, providing no local variables conflict:
using namespace myth;
cin >> fetch; // really std::cin and Jill::fetch
Now consider applying a using-directive to the myth namespace. The using-directive is
transitive. We say an operation op is transitive if A op B and B op C implies A op C. For
example, the > operator is transitive. (That is, A bigger than B and B bigger than C implies
A bigger than C.) In this context, the upshot is that the statement
using namespace myth;
results in the elements namespace being added via a using-directive also, so it's the same
as the following:
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
using namespace myth;
using namespace elements;
You can create an alias for a namespace. For example, suppose you have a namespace
defined as follows:
namespace my_very_favorite_things { };
You can make mvft an alias for my_very_favorite_things with the following statement:
namespace mvft = my_very_favorite_things;
You can use this technique to simplify using nested namespaces:

namespace MEF = myth::elements::fire;
using MEF::flame;
Unnamed Namespaces
You can create an unnamed namespace by omitting the namespace name:
namespace // unnamed namespace
{
int ice;
int bandycoot;
}
This behaves as if it were followed by a using-directive; that is, the names declared in this
namespace are in potential scope until the end of the declarative region containing the
unnamed namespace. In this respect, they are like global variables. However, because the
namespace has no name, you can't explicitly use a using-directive or using-declaration to
make the names available elsewhere. In particular, you can't use names from an unnamed
namespace in a file other than the one containing the namespace declaration. This
provides an alternative to using static variables with internal linkage. Indeed, the C++
standard deprecates the use of the keyword static in namespaces and global scope.
("Deprecate" is a term the standard uses to indicate practices that currently are valid but
most likely will be rendered invalid by future revisions of the standard.) Suppose, for
example, you have this code:
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
static int counts; // static storage, internal linkage
int other();
int main()
{
}
int other()
{
}
The intent of the standard is that you should do this instead:

namespace
{
int counts; // static storage, internal linkage
}
int other();
int main()
{
}
int other()
{
}
A Namespace Example
Let's take a look at a multifile example that demonstrates some of the namespace features.
The first file is a header file that contains some items normally found in header
files—constants, structure definitions, and function prototypes. In this case the items are
placed in two namespaces. The first namespace, called pers, contains a definition of a
Person structure, plus prototypes for a function that fills a structure with a person's name
and a function that displays the structure contents. The second namespace, called debts,
defines a structure for storing the name of a person and the amount of money owed to that
person. This structure uses the Person structure, so, the debts namespace has a
using-directive to make the names in the pers available in the debts namespace. The
debts namespace also contains some prototypes. Listing 9.9 shows this header file.
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
Listing 9.9 namesp.cpp
// namesp.h
namespace pers
{
const int LEN = 40;
struct Person
{

char fname[LEN];
char lname[LEN];
};
void getPerson(Person &);
void showPerson(const Person &);
}
namespace debts
{
using namespace pers;
struct Debt
{
Person name;
double amount;
};
void getDebt(Debt &);
void showDebt(const Debt &);
double sumDebts(const Debt ar[], int n);
}
The next file follows the usual pattern of having a source code file provide definitions for
functions prototyped in a header file. The function names, being declared in a namespace,
have namespace scope so the definitions need to be in the same namespace as the
declarations. This is where the open nature of namespaces comes in handy. The original
namespaces are brought in by including namesp.h (Listing 9.9). The file then adds the
function definitions to the two namespaces, as shown in Listing 9.10.
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
Listing 9.10 namesp.cpp
// namesp.cpp namespaces
#include <iostream>
using namespace std;
#include "namesp.h"

namespace pers
{
void getPerson(Person & rp)
{
cout << "Enter first name: ";
cin >> rp.fname;
cout << "Enter last name: ";
cin >> rp.lname;
}
void showPerson(const Person & rp)
{
cout << rp.lname << ", " << rp.fname;
}
}
namespace debts
{
void getDebt(Debt & rd)
{
getPerson(rd.name);
cout << "Enter debt: ";
cin >> rd.amount;
}
void showDebt(const Debt & rd)
{
showPerson(rd.name);
cout <<": $" << rd.amount << endl;
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
}
double sumDebts(const Debt ar[], int n)
{

double total = 0;
for (int i = 0; i < n; i++)
total += ar[i].amount;
return total;
}
}
Finally, the third file of the program is a source code file using the structures and functions
declared and defined in the namespaces. Listing 9.11 shows several methods of making
the namespace identifiers available.
Listing 9.11 namesp.cpp
// usenmsp.cpp using namespaces
#include <iostream>
using namespace std;
#include "namesp.h"
void other(void);
void another(void);
int main(void)
{
using debts::Debt;
using debts::showDebt;
Debt golf = { {"Benny", "Goatsniff"}, 120.0};
showDebt(golf);
other();
another();
return 0;
}
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
void other(void)
{
using namespace debts;

Person dg = {"Doodles", "Glister"};
showPerson(dg);
cout << endl;
Debt zippy[3];
int i;
for (i = 0; i < 3; i++)
getDebt(zippy[i]);
for (i = 0; i < 3; i++)
showDebt(zippy[i]);
cout << "Total debt: $" << sumDebts(zippy, 3) << endl;
return;
}
void another(void)
{
using pers::Person;;
Person collector = { "Milo", "Rightshift" };
pers::showPerson(collector);
cout << endl;
}
First, main() using two using-declarations:
using debts::Debt; // makes the Debt structure definition available
using debts::showDebt; // makes the showDebt function available
Note that using-declarations just use the name; for example, the second one doesn't
describe the return type or function signature for showDebt; it just gives the name. (Thus,
if a function were overloaded, a single using-declaration would import all the versions.)
Also, although both Debt and showDebt() use the Person type, it isn't necessary to
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
import any of the Person names because the debt namespace already has a
using-directive including the pers namespace.
Next, the other() function takes the less desirable approach of importing the entire

namespace with a using-directive:
using namespace debts; // make all debts and pers names available to other()
Because the using-directive in debts imports the pers namespace, the other() function
can use the Person type and the showPerson() function.
Finally, the another() function uses a using-declaration and the scope resolution operator
to access specific names:
using pers::Person;;
pers::showPerson(collector);
Here is a sample run of the program:
Goatsniff, Benny: $120
Glister, Doodles
Enter first name: Arabella
Enter last name: Binx
Enter debt: 100
Enter first name: Cleve
Enter last name: Delaproux
Enter debt: 120
Enter first name: Eddie
Enter last name: Fiotox
Enter debt: 200
Binx, Arabella: $100
Delaproux, Cleve: $120
Fiotox, Eddie: $200
Total debt: $420
Rightshift, Milo
Namespaces and the Future
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
As programmers become familiar with namespaces, common programming idioms will
emerge. In particular, many hope that use of the global namespace will wither away and
that class libraries will be designed by using the namespace mechanisms. Indeed, current

C++ already calls for placing standard library functions in a namespace called std.
As mentioned before, changes in the header filenames reflect these changes. The older
style header files, such as iostream.h, do not use namespaces, but the newer iostream
header file should use the std namespace.
Summary
C++ encourages you to use multiple files in developing programs. An effective
organizational strategy is to use a header file to define user types and provide function
prototypes for functions to manipulate the user types. Use a separate source code file for
the function definitions. Together, the header file and the source file define and implement
the user-defined type and how it can be used. Then, main() and other functions using
those functions can go into a third file.
C++'s storage schemes determine how long variables remain in memory (storage duration)
and what parts of a program have access to them (scope and linkage). Automatic variables
are those defined within a block, such as a function body or a block within the body. They
exist and are known only while the program executes statements in the block that contains
the definition. Automatic variables may be declared using the storage class specifiers auto
or register or with no specifier at all, which is the same as using auto. The register
specifier is a hint to the compiler that the variable is heavily used.
Static variables exist for the duration of the program. A variable defined outside of any
function is known to all functions in the file following its definition (file scope) and is made
available to other files in the program (external linkage). For another file to use such a
variable, that file must declare it by using the extern keyword. A variable shared across
files should have a defining declaration in one file (extern not used) and reference
declarations in the other files (extern used). A variable defined outside any function but
qualified with the keyword static has file scope but is not made available to other files
(internal linkage). A variable defined inside a block but qualified with the keyword static is
local to that block (local scope, no linkage) but retains its value for the duration of the
program.
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.
C++ functions, by default, have external storage class, so they can be shared across files.

But functions qualified with the keyword static have internal linkage and are confined to the
defining file.
Namespaces let you define named regions in which you can declare identifiers. The intent
is to reduce name conflicts, particularly in large programs using code from several vendors.
Identifiers in a namespace can be made available by using the scope resolution operator,
by using a using-declaration, or by using a using-directive.
Review Questions
.1:What storage scheme would you use for the following situations?
homer is a formal argument (parameter) to a function. a.
The secret variable is to be shared by two files. b.
The topsecret variable is to be shared by the functions in one file but
hidden from other files.
c.
beencalled keeps track of how many times the function containing it
has been called.
d.
.2:Discuss the differences between a using-declaration and a using-directive.
.3:Rewrite the following so that it doesn't use using-declarations or
using-directives.
#include <iostream>
using namespace std;
int main()
{
double x;
cout << "Enter value: ";
while (! (cin >> x) )
This document was created by an unregistered ChmMagic, please go to to register it. Thanks.

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×