Type specifiers determine the type of the name being declared.
type-specifier:
simple-type-name
class-specifier
enum-specifier
elaborated-type-specifier
::class-name
const
volatile
This section discusses simple type names, elaborated type specifiers, and nested type names.
A simple type name is the name of a complete type.
simple-type-name:
complete-class-name
qualified-type-name
char
short
int
long
signed
unsigned
float
double
void
Table 6.2 shows how the simple type names can be used together.
A name specified using the const keyword has internal linkage unless it is specifically given external linkage.
Table 6.2 Type Name Combinations
Type | Can Appear With | Comments |
int | long or short, but not both | Type int can be used alone, in which case it means short int in 16-bit compilations and long int in 32-bit compilations. | |
long | int or double | Type long implies type long int. | |
short | int | Type short implies type short int. | |
signed | char, short, int, or long, Type signed implies signed int. The most-significant bit of objects of type signed char and bit fields of signed integral types is taken to be the sign bit. | ||
unsigned | char, short, int, or long, Type unsigned implies unsigned int. The most-significant bit of objects of type unsigned char and bit fields of unsigned integral types is not treated as the sign bit. |
Elaborated type specifiers are used to declare user-defined types. These can be either class- or enumerated-types.
elaborated-type-specifier:
class-key class-name
class-key identifier
enumenum-name
class-key:
class
struct
union
If identifier is specified, it is taken to be a class name. For example:
class Window;
The above statement declares the Window identifier as a class name. This syntax is used for forward declaration of classes. For more information about class names, see “Class Names” in Chapter 8, on topic .
If a name is declared using the union keyword, it must be defined using the union keyword as well. Names that are defined using the class keyword can be declared using the struct keyword (and vice versa). Therefore, the following code samples are legal:
// Legal example 1
struct A; // Forward declaration of A.
class A // Define A.
{
public:
int i;
};
// Legal example 2
class A; // Forward declaration of A.
struct A // Define A.
{
private:
int i;
};
// Legal example 3
union A; // Forward declaration of A.
union A // Define A.
{
int i;
char ch[2];
};
The next set of examples, however, are illegal:
// Illegal example 1
union A; // Forward declaration of A.
struct A // Define A.
{
int i;
};
// Illegal example 2
union A; // Forward declaration of A.
class A // Define A.
{
public:
int i;
};
// Illegal example 3
struct A; // Forward declaration of A.
union A // Define A.
{
int i;
char ch[2];
};
Microsoft C++ supports declaration of nested types—both named and anonymous.
qualified-type-name:
typedef-name
class-name :: qualified-type-name
complete-class-name:
qualified-class-name
:: qualified-class-name
qualified-class-name:
class-name
class-name :: qualified-class-name
In some programming situations, it makes sense to define nested types. These types are visible only to member functions of the class type in which they are defined. They can also be made visible by constructing a qualified type name using the scope-resolution operator (::).
Note :
One commonly used class hierarchy that employs nested types is iostream. In the iostream header files, the definition of class ios includes a series of enumerated types, which are packaged for use only with the iostream library.
The following is an example of how to define nested classes:
class WinSystem
{
public:
class Window
{
public:
Window(); // Default constructor.
~Window(); // Destructor.
int NumberOf(); // Number of objects of class.
int Count(); // Count number of objects of class.
private:
static int _Count;
};
class CommPort
{
public:
CommPort(); // Default constructor.
~CommPort(); // Destructor.
int NumberOf(); // Number of objects of class.
int Count(); // Count number of objects of class.
private:
static int _Count;
};
};
// Initialize WinSystem static members.
int WinSystem::Window::_Count = 0;
int WinSystem::CommPort::_Count = 0;
To access a name defined in a nested class, use the scope-resolution operator (::) to construct a complete class name. Use of this operator is shown in the initializations of the static members in the example above. To use a nested class in your program, use code such as the following:
WinSystem::Window Desktop;
WinSystem::Window AppWindow;
cout << “Number of active windows: ” << Desktop.Count() << “\n”;
Nested anonymous classes or structures can be defined as follows:
class Ledger
{
class
{
public:
double PayableAmt;
unsigned PayableDays;
} Payables;
class
{
public:
double RecvableAmt;
unsigned RecvableDays;
} Receivables;
};
An anonymous class must be an aggregate, which has no member functions and no static members.
Note :
While an enumerated type can be defined inside a class declaration, the reverse is not true; class types cannot be defined inside enumeration declarations.