bool Keyword |
void main(){
bool b=true; // Declare a variable of type bool and set it to true.
if (b) // Test value of bool variable.
b=false; // Set it to false.
}
dynamic_cast Keyword |
class Base {
virtual void f(); // Make Base a polymorphic type.
// other class details omitted
};
class Derived : public Base {
// class details omitted
};
void Base::f()
{
// define Base function
}
void main()
{
Base *p;
Derived *q;
Base b;
Derived d;
p = &b;
q = dynamic_cast<Derived *> (p); // Yields zero.
p = &d;
q = dynamic_cast<Derived *> (p); // Yields p treated
// as a derived pointer.
}
Static and dynamic casts are used to move within a class hierarchy. Static
casts use only static (compile-time) information to do the conversions.
In the example above, if p is really pointing to an object of type Derived,
either a static or dynamic cast of p to q yields the same result. This
is also true if p were the null pointer. But, if p is not pointing to an
object of type Derived, a dynamic cast returns zero, and a static cast
returns a stray pointer. Dynamic casts must be done to a pointer or reference
type. For example, if the cast above is written as:
q = dynamic_cast <Derived> (p);The compile time error message is:
The result type of a dynamic cast must be a pointer or reference to a complete class; the actual type was Derived.If you attempt a dynamic cast from a non-polymorphic type, you will also get a compile-time error. For example:
class Base {
// class details omitted
};
class Derived : public Base {
// class details omitted
};
void main()
{
Base *p;
Derived *q;
Base b;
p = &b;
q = dynamic_cast<Derived *> (p);
}
The above generates a compile-time error stating that:
Dynamic down-casts and cross-casts must start from a polymorphic class (one that contains or inherits a virtual function); but class Base is not polymorphic.The syntax of conditions allows declarations in them. For example:
class Base {
virtual void f(); // Make Base a polymorphic type
// other class details omitted
};
class Derived : public Base {
public:
void derivedFunction();
// other class details omitted
};
void Base::f()
{
// Define Base function.
}
void Derived::derivedFunction()
{
}
void main()
{
Base *p = new Derived;
// details omitted
if (Derived *q = dynamic_cast<Derived *> (p))
q->derivedFunction(); // use derived function
}
You can use dynamic casts with references as well. Since a reference can't
be zero, when the cast fails it raises a Bad_cast exception. Before the
implementation of the dynamic cast operator, you could not cast from a
virtual base class to one of its derived classes because there was not
enough information in the object at runtime to do this cast. Once runtime
type identification was added, however, the information stored in a polymorphic
virtual base class is sufficient to allow a dynamic cast from this base
class to one of its derived classes. For example:
class Base1 {
// Not a polymorphic type.
// additional class details omitted
};
class Base2 {
virtual void f(); // Make Base2 polymorphic.
// additional class details omitted
};
void Base2::f()
{
// Define Base2 function.
}
class Derived : public virtual Base1, public virtual Base2 {
// additional class details omitted
};
void main()
{
Base1 *bp1;
Base2 *bp2;
Derived *dp;
bp1 = new Derived;
bp2 = new Derived;
// dp = (Derived *) bp1; // Problem: compile time error
// Can't cast from virtual base.
// dp = (Derived *) bp2; // Problem: compile time error
// Can't cast from virtual base.
// dp = dynamic_cast<Derived *> bp1; // Problem: compile time error
// Can't cast from
// non-polymorphic type.
dp = dynamic_cast<Derived *> bp2; // OK
}
explicit Keyword |
The explicit keyword is used for declaring constructor functions within class declarations. When these functions are declared explicit, they cannot be used for implicit conversions.
class C {
public:
explicit C(int);
};
C::C(int)
{
// empty definition
}
void main()
{
C c(5); // Legal
c = C(10); // Legal
// c = 15; // Produces a compile time error:
// Message: Cannot assign 'C' with 'int'.
// c + 20; // Produces a compile time error
}
A classic example of this problem is an array class:
class Vector {
public:
Vector(int n); // create a vector of n items
// other class details omitted
};
void main()
{
Vector operator + (Vector, Vector);
Vector v1(10), v2(10); // create two 10 element vectors
// details omitted
v1 = v2 + 5; // Legal - converts int 5 to a 5
// element vector and adds to v2.
// Not something you want to be
// legal
}
With the explicit keyword, the constructor can be made explicit and the
declarations are legal, but the addition is a compilation error:
class Vector {
public:
explicit Vector(int n); // create a vector of n items
// other class details omitted
};
void main()
{
Vector operator + (Vector, Vector);
Vector v1(10), v2(10); // create two 10 element vectors
// details omitted
// v1 = v2 + 5; // Not legal - generates compile-
// time error
// Message: Illegal types
// associated with operator '+':
// 'Vector' and 'int'.
}
mutable Keyword |
An example of this is a use or reference count in an object that keeps track of the number of pointers referring to it.
class C {
public:
C();
int i;
mutable int j;
};
C::C() : i(1), j(3)
{
// Define constructor
}
void main()
{
const C c1;
C c2;
// c1.i =0; // Problem: compilation error
// Message: The left side of '=' must be
// a modifiable lvalue.
c1.j = 1; // OK
c2.i = 2; // OK
c2.j = 3; // OK
}
The mutable keyword can only be used on class data members. It cannot be
used for const or static data members. Notice the difference in the two
pointer declarations below:
class C {
C() { } // define constructor
mutable const int *p; // OK
// mutable pointer to int const
// p in constant C object can be modified
mutable int *const q; // Compile time error
// mutable const pointer to int
// const data member can't be mutable
// Message: 'mutable' may be used only
// in non-static and non-constant data
// member declarations within class
// declarations.
};
namespace and using Keywords |
As can be seen, every namespace introduces a new scope. By default, names inside a namespace are hidden from enclosing scopes. Selection of a particular name can be achieved using the qualified-name syntax.
Namespaces can be nested very much like classes.
#include <stdio.h>
namespace N {
struct Object {
virtual char const* name() const { return "Object from N"; }
};
}
namespace M {
struct Object {
virtual char const* name() const { return "Object from M"; }
};
namespace X { // a nested namespace
struct Object: M::Object { // inherit from a class in the outer space
char const* name() const { return "Object from M::X"; }
};
}
}
int main() {
N::Object o1;
M::Object o2;
M::X::Object o3;
printf("This object is: %s.\n", o1.name());
printf("This object is: %s.\n", o2.name());
printf("This object is: %s.\n", o3.name());
return 0;
}
One somewhat unique feature of namespaces is that they can be extended. The example below shows this as well as the connections between a namespace extending across different translation units.
The example also illustrates the concept of so-called unnamed namespaces. These namespaces can only be extended within a translation unit. Unnamed namespaces in different translation units are unrelated; hence their names effectively have internal linkage. In fact, the ANSI/ISO C++ International Standard specifies that using static to indicate internal linkage is deprecated in favor of using namespaces.
#include <stdio.h>
namespace N {
char const* f() { return "f()"; }
}
namespace { // An unnamed namespace
char const* f(double);
} // Names in unnamed namespaces are visible in their surrounding scope.
// They cannot be qualified since the space has no name.
namespace N { // An extension of the first part of namespace N
char const* f(int); // Leave the implementation to another
} // translation unit.
int main() {
printf("Calling: %s.\n", N::f()); // OK, declared and defined above
printf("Calling: %s.\n", N::f(7)); // OK, declared above (defined elsewhere)
printf("Calling: %s.\n", f(3.0)); // OK, declared above (defined below)
return 0;
}
namespace { // An extension of the unnamed namespace in this translation unit
char const* f(double) { return "f(double) in main() translation unit"; }
}
namespace { // An unnamed namespace unrelated to the one in the other
// translation units.
char const* f(double) { return "f(double) in auxiliary translation unit"; }
}
namespace N { // This namespace is the same as the one in the main()
// translation unit. We implement f(int) here.
char const* f(int) { return "f(int) defined in auxiliary translation unit"; }
}
using N::x; // Where N is a namespace, x is a name in N After this declaration, all uses of x in this scope are taken to defer to N::x. (The N:: prefix is no longer required.)If another declaration of x were introduced in the same scope, for example:
int x; then a compiler error would occur.
using namespace N; // If not found, lookup names in namespace N If x is a name in namespace N, but another declaration of x is present in the current scope, for example:
int x; a compiler error is not necessarily emitted. Only if that name is used will an ambiguity occur.CAUTION: Using-directives are transitive. If you specify a using-directive to one namespace which itself specifies a directive to another namespace, then names used in your scope will also be looked up in that other namespace.
Using namespace directives can be a powerful means to migrate code to libraries that use namespaces. Occasionally, however, they may silently make unwanted names visible. It is therefore often suggested not to use using-directives unless the alternatives are very inconvenient.
#include <stdio.h>
namespace N {
char const* f() { return "N::f()"; }
char const* f(double) { return "N::f(double)"; }
char const* g() { return "N::g()"; }
}
char const* g(double) {
using N::f; // Declare all f's in namespace N
return f(2.0);
}
namespace M { // Illustrate how using-directives
using namespace N; // are transitive
}
int main() {
using namespace N;
printf("Calling: %s.\n", f()); // calls N::f()
printf("Calling: %s.\n", g(1.0)); // calls ::g(double)
// which calls
// N::f(double)
printf("Calling: %s.\n", N::g()); // calls N::g()
printf("Calling: %s.\n", M::f()); // calls N::f()
return 0;
}
typeid Keyword |
# include <iostream.h>
# include <typeinfo>
class Base {
virtual void f(); // Must have a virtual function to
// be a polymorphic type.
// additional class details omitted
};
class Derived : public Base {
// class details omitted
};
void Base::f()
{
// Define function from Base.
}
void main ()
{
Base *p;
// Code which does either
// p = new Base; or
// p = new Derived;
// Note that this is NOT a good design for this functionality.
// Virtual functions would be better.
if (typeid(*p) == typeid(Base))
cout << "Base Object\n";
else if (typeid(*p) == typeid(Derived))
cout << "Derived Object\n";
else
cout << "Another Kind of Object\n";
}
If a typeid operation is performed on an expression that is not a polymorphic
type (a class which declares or inherits a virtual function), the operation
returns the static (compile-time) type of the expression. In the example
above, if class Base did not include the virtual function f(), typeid(p)
would always yield the type Base.
The style of programming used in the above example might be called a typeid switch statement. It is not generally a reasonable design. One alternative is to use a virtual function in a base class specialized in each of its derived classes. In some cases, this may not be possible, for example, when the base class is provided by a library for which source code is not available. In other cases it may not be desirable, for example, some base class interfaces might be too big if all derived class functionality is included.
You could rewrite the above example, using virtual functions, as:
class Base {
virtual void outputType() { cout << "Base Object\n"; }
// additional class details omitted
};
class Derived : public Base {
virtual void outputType() { cout << "Derived Object\n"; }
// additional class details omitted
};
void main ()
{
Base *p;
// code which does either
// p = new Base; or
// p = new Derived;
p->outputType();
}
A second alternative is to use a dynamic cast. In many cases, this alternative
is less desirable than using virtual functions, but it is better than a
typeid switch statement in nearly every case. There is a subtle difference
between this alternative and the typeid switch statement above. The typeid
operation allows access to the exact type of an object; a dynamic cast
returns a non-zero result for the target type or a type publicly derived
from it.
You could rewrite the above example as follows using dynamic casts:
class Base {
virtual void f(); // Must have a virtual function to
// be a polymorphic type.
// additional class details omitted
};
class Derived : public Base {
// class details omitted
};
void Base::f()
{
// Define function from Base.
}
void main ()
{
Base *p;
// code which does either
// p = new Base; or
// p = new Derived;
if (dynamic_cast <Derived *> (p))
cout << "Derived (or class derived from Derived) Object\n";
else
cout << "Base Object\n";
}
volatile Keyword |
This keyword is part of the ANSI C standard with the same syntax and semantics.
Note, you can declare an identifier to be both const and volatile. This declares a value that the program cannot change but which can be changed by some means external to the program (such as by a piece of hardware like a clock).
class C {
public: // public to make example simpler
volatile int i;
// other class details omitted
};
C someData[10];
void main ()
{
int j = someData[5].i;
j = someData[5].i; // Without the volatile specifier, the
// compiler could optimize these two
// statements into one. With it, it must
// execute both in case the i field of
// someData[5] has changed by some
// other means.
}
wchar_t Keyword |
void main()
{
wchar_t ch = L'a';
}
wchar_t must be implemented the same as another integral type.
In other words, it must have the same size, signedness and alignment requirements.
It promotes to the smallest integral type when used in an expression and
cannot have a signed or unsigned modifier.
The standard library includes a string of wide characters known as wstring. The IOStream library supports I/O of wide characters.
In ANSI C, wchar_t is a synonym for another type, declared using a typedef in a standard header file.
typename Keyword |
template<class T>
class C1 {
// class details omitted
// T::C2 *p; // Problem: flagged as compile-time
// error. T is a type, but T::C2 is not.
// Message: 'C2' is used as a type, but
// has not been defined as a type.
typename T::C2 *p; // Solution: the keyword typename flags
// the qualified name T::C2 as a type.
};
class C {
// details omitted
class C2 {
//details omitted
};
};
void main ()
{
C1<C> c;
}
template<class T>
class C {
// Additional details omitted
};
template<class T>
class C {
// Additional details omitted
};
template<class T>
class C1 {
class C2;
// Additional details omitted
};
class C1;
template<class T>
class C2 {
// details omitted
};
Overloading new[ ] and delete[ ] for Arrays |
# include <iostream.h>
class C {
public:
void* operator new[ ] (size_t); // new for arrays
void operator delete[ ] (void*); // delete for arrays
// additional class details omitted
};
void* C::operator new[ ] (size_t allocSize)
{
cout << "Use operator new[ ] from class C\n";
// here, real usage would include allocation
return ::operator new[ ] (allocSize); // global operator
} // for this simple example
void C::operator delete[ ] (void *p)
{
cout << "Use operator delete[ ] from class C\n";
// here, real usage would include deallocation
::operator delete[ ] (p); // global operator
} // for this simple example
void main()
{
C *p;
p = new C[10];
delete[ ] p;
}
Notice that the new operator takes a class with an array specifier as an
argument. The compiler uses the class and array dimension to provide the
size_t argument. In the example above, the argument provided is ten times
the size of a class C object. Also, the operator must return a void* which
the compiler converts to the class type. The void constructor for the class
(if one exists) is invoked to initialize the elements in the array.
Multidimensional arrays can be allocated and deallocated with these operators. The operator is used with several array dimensions, and the compiler provides the size_t argument which is the space required for the entire array. For example:
// call C::operator new[ ] ( ) with // an argument of 10 * 20 * sizeof(C) p = new C [10] [20];Additional arguments can be provided to this operator new just as for the operator for single objects. In this way, the operator can be overloaded in a class. The additional arguments can be used by the storage allocation scheme for additional storage management.
The global new and delete for both arrays and single objects are provided in the Standard C++ Library. This library also provides a version of new for arrays and single objects that takes a second void* argument and constructs the object at that address.
Standard Exception Classes |
# include <stdexcept>
# include <iostream>
# include <string>
void f()
{
// details omitted
throw range_error(string("some info"));
}
void main()
{
try {
f();
}
catch (runtime_error& r) {
// handle any kind of runtime error including range_error
cout << r.what() << '\n';
}
}
Each of the subclasses includes a constructor taking an instance of the Standard C++ Library string class as an argument. They initialize an instance such that the function what(), when applied to the instance, returns a value equal to the argument to the constructor.
Exceptions Thrown by the Standard C++ Library |
type_info Class |
# include <iostream.h>
# include <typeinfo>
class Base {
virtual void f(); // Must have a virtual function to
// be a polymorphic type
// additional class details omitted
};
class Derived : public Base {
// class details omitted
};
void Base::f()
{
// Define function from Base.
}
void main ()
{
Base *p;
// code which does either
// p = new Base; or
// p = new Derived;
if (typeid(*p) == typeid(Base)) // Standard requires
// comparison as part of
// this class.
cout << "Base Object\n";
cout << typeid(*p).name() << '\n'; // Standard requires access to
// the name of the type.
}
The standard requires the class type_info to be polymorphic. You
can't assign or copy instances of the class (the copy constructor and assignment
operators are private). The interface must include:
int operator == (const type_info&) const int operator !=( const type_info&) const const char * name() const int before (const type_info&) constThe operators allow comparison of object types. The name() function allows access to the character string representing the name of the object. The before function allows types to be sorted. This allows them to be accessed through hash tables. The before function is not a lexical ordering; it might not yield the same results. The name() now returns the mangled name of a type per the C++ ABI. This string can not be demangled with __cxa_demangle.
Unsupported Functionality |
Functionality defined in the ANSI/ISO C++ International Standard and not supported in this release of HP aC++ is listed below. Library functionality is listed separately.
NOTE: These are not all inclusive lists.
| Functionality | Rogue Wave Standard C++ Library 2.2.1 | Rogue Wave Standard C++ Library 1.2.1 | libc **
(HP-UX System Libraries) |
|---|---|---|---|
| <allocator> | Yes. | Provided as a class rather than a template. | Not applicable. |
| <cstring> | Yes. | The following C++ overloaded functions are not provided, instead, ANSI
C signatures are implemented.
|
Not applicable. |
| <cwchar> | Yes. | The following C++ overloaded functions are not provided, instead, ANSI
C signatures are implemented.
|
For missing functions, see wide character support in this table. |
| <cwctype> | Partial support. | Partial support. | See wide character support in this table. |
| <functional> | Yes. | The following types are not provided:
|
Not applicable. |
| <iostream> | Yes. | Not templatized and the following headers are not provided:
|
Not applicable. |
| <iterator> | Yes. | iterator template is not provided. | Not applicable. |
| <locale > | Yes. | Not provided. | Not applicable. |
| printf(3) formats | Not applicable. | Not applicable. | %ls and %lc are not provided |
| <utility> | Yes. | rel_ops namespace is not supported. | Not applicable. |
| <valarray> | Yes. | Not provided. | Not applicable. |
| wide character support | Not applicable. | Not applicable. | The following functions are not provided:
|
** Available when compiled with -D_XOPEN_SOURCE=500. Also, the application must be linked with /usr/lib/hpux##/unix98.o, where ## is either 32 or 64.