Aug 22, 2001
C++ Class Design Helper
Contents
Class Declaration
#include
class Id1 { //Class naming conventions
private:
//Declarations
for private members of this class - member data, and member functions
//Optional inline functions
public:
//Declarations
for the public member this class, including
constructor functions .
Id1 ( );
//Sample constructor for the Id1 class
//Optional copy constructor
//Optional inline functions
//Optional destructor
}; //End of the
class declaration for class Id1
//Optional inline functions
using the inline keyword
Class Objects - i.e., instances of classes:
const Objects:
An object can be declared const. That means that its data members cannot be
altered. Any member function provided to access a const object must be a
const member function.
Access Specifiers
The public and private access specifiers specify the visibility of the
members that follow. In a class
declaration, the access is private by default until an explicit access
specifier is encountered.
public:
Public members can be accessed by member functions and by functions that
declare an instance of the class.
A class member that is public static is accessible to the entire program
and is not associated with a specific instance of the class. A public static
member function is not quite global. It exists only within the scope of the
class in which it is defined, and it can be called from anywhere within that
scope using the :: scope resolution operator.
private:
Private members can only be accessed by member functions of the class.
protected:
Protected members are not available to users of the class. If a program
instantiates an object, the program cannot access the protected members of that
object. In this respect, protected members are the same as private members. If
a base class has private members, those members are not accessible to the
member functions of any derived class. Protected members are public to member
functions of classes derived from the base class and private to the rest of the
program. When designing a class, consider whether the class may someday be derived
from - even if you initially have no such intentions. Specify the protected keyword for members
that could be accessible to derived classes.
Arrays of Class Objects
Declare an array of classes as follows:
Class_name
instance_name[ #OfElements];
//The default constructor function
will be called once for each element of the array
In order to support arrays of class objects, the class must have a default constructor – either one with no
arguments, or one with default arguments. [The notation for declaring an array
of objects does not provide for an initializer list
that conforms to the format of constructor function arguments.]
Class Naming Conventions
Capitalization:
Class names are typically begun with capital letters. This is to help
distinguish between classes and member data variables and member functions,
which are typically begun with lower case letters.
Constructor Functions
The constructor function has the job if initializing the data members of
the class when an object is instantiated. The constructor has the same name as
the class and is automatically executed when an object of the class is instantiated. The constructor
function has no return value.
The constructor function is always public (except in the case of private
classes, which have private constructors.). The compiler will error if you try
to instantiate an object if the constructor is not public. It is illogical to have a constructor that
is not public – there would be no way to instantiate objects and no way to
derive classes from it. A constructor
cannot be virtual. A constructor cannot have a return type – not even void.
Don’t try to perform too many tasks with a constructor. A constructor
should only be concerned with initializing values for an object. It shouldn’t
be performing the main task of the object.
If the constructor function requires parameters, the instantiation includes
initializer arguments corresponding to the parameter list in the constructor's
prototype. The simplest constructor is none at all. In that case the compiler
provides a public default constructor that
usually does nothing. The next level of constructor complexity is a constructor
that you provide with an empty parameter list, which does nothing. In that case
the instantiation of the object leaves off the parenthesis. The next level of
complexity is a constructor that
provides default values for all of its
parameters. For example:
Box(int ht=0, int wd = 0, int dp = 0);
Overloaded Constructors:
A class can have more than one constructor function, which means that the
constructor is "overloaded". Overloaded constructors may provide
different parameter lists with either a different number of arguments or with
arguments of different type including the empty parameter list.
Conversion Constructors:
A conversion constructor accepts one and only one argument, which must
be of a different type than the class of the constructor.
Class_name::Class_name ( type arg1)
{
//Code that
does the actual conversion of the arg1 into an object of type Class_name
}
Context in which a Conversion Constructor would be used:
Date dt(6, 29, 90); //Date
object, dt
DayOfYear dy; //DayOfYear
object dy
dy = dt; //In
all three of these, the conversion constructor would be used by the compiler to
make the conversion
dy = (DayOfYear) dt;
dy = DayOfYear(dt);
Copy Constructors:
The purpose of a copy constructor is to copy an existing object of a class
to a newly instantiated object of the same class. The compiler provides a
default copy constructor if you do not. The default copy constructor simply
makes a member by member copy of the source object's data members. The danger
of such a default copy constructor is that the data member may be a dynamically
created (via "new") array of
char and it is the pointer to the array that gets copied by the default copy
constructor. That results in two pointers pointing to the same memory location
in the heap. The first of the objects that goes out of scope causes the array's
memory in the heap to be de-allocated (returned to the free store), but when
the next object goes out of scope and the destructor tries to de-allocate that
same memory, the results are unpredictable. Such misadventures can be avoided
by implementing a constructor that allocates the appropriate amount of memory
for the new object's data member, using "new", and then copies the
original object's data member value(s) to the new variable. In that way, when
the destructor is called, the allocated memory for each object is separate and
unique.
Derived Class Constructors:
The compiler causes execution of the base class’s constructor before it
executes the derived class’s constructor. Therefore the constructor function of
the derived class must tell the compiler what values to use as arguments to the
constructor function for the base class.
The derived class’s constructor specifies the arguments to the base
class’s constructor function using a parameter initialization list for the base
class, as shown here:
SpecialDayOfYearDate(int yr, int da) :
Special Date(0, 0, 0)
/* Where
SpecialDayOfYearDate is the derived class
and SpecialDate is
the base class */
{
}
The constructor function declaration in a class derived from multiple bases
specifies the arguments for the constructors of all the base classes, as shown
here:
CompanyCar :: CompanyCar ( int vehno, int assetno) :
Vehicle(vehicleno) , Asset (assetno)
{
//…
}
The order of execution of constructors in the above example is
Vehicle, then Asset and CompanyCar is
last.
Data Members of a Class
Member Data:
Member data can be any valid C++ data type, including an instance of
another class or a pointer or a reference. Member data can be declared static,
const or both.
static Data Members:
Declaring a data member as static means that it is a class-wide variable
rather than a per instance variable. That is, all objects of the class access
the same variable, whereas each instance of a class receives its own instance
of each non-static variable. A static
data member must be defined from outside the class declaration using the scope
resolution operator and the class name. [See the exception to this in the case
of static const data
members.] For example,
datatype
class_name::static_variable_name =
…..; // Do not use the keyword static
here
The static variable would have already been declared inside the class, as
follows:
class_name {
…
static datatype
static_variable_name; //Use the keyword
static here
…
}
Only one copy of a static data member exists, regardless of how many
objects of the class are instantiated. Do not confuse static data members with static storage or static linkage. They are independent of one another.
const Data Members:
Data members of a class can be declared const by using the keyword const
before the datatype. Constant data members must be initialized. In order to
initialize a const data member in the constructor, you use a parameter
initialization list.
static const Data Members:
Data members that are static can be further qualified as const
and can be initialized directly in the class declaration. In that case, the
data member does not need to be defined outside of the class definition.
explicit Constructors:
Class Time {
//…
explicit
Time(int secs);
};
Without the keyword, explicit, the compiler would make an implicit,
silent conversion when an integral value is found where a Time object was
expected.
Time time(21); //OK
– explicit construction
time = 12; //Error,
implicit construction – in other words, the compiler would error out saying
that it cannot do the implicit conversion.
protected Data Members:
The protected access specifier is a measure that may be used to support
subsequent derivations from a class.
Derived Classes
Declaration of Derived Classes:
classDerived : public
BaseClass {
//…A class
derived from class BaseClass
};
The constructor(s) and destructor(s) of the base class must be public (or protected).
[If the constructor is protected, the class will be an abstract base class.]
Characteristics in a class that will enhance its role as a base class:
Use of the virtual qualifier with the
destructor and other member functions.
Use of the protected access qualifier with
data members.
public or private reference to
the Base Class:
In the declaration of the derived class, the use of the public access
specifier means that the protected members of the base class are protected
members of the derived class and the public members of the base class are
public members of the derived class.
The private access specifier means that the protected and public members
of the base class are private members of the derived class.
Destructors
When a class object goes out of scope (including use of the delete
keyword), the destructor function is called.
The destructor function has the name of the class prefixed by the
"~", and there is only one destructor function for a class.
A destructor function has no parameters and does not return a value (no
return type).
An important rationale' for a destructor is to return memory to the heap
(free store). Data members that were created dynamically by the class object
via the "new" keyword should be deleted (returned to the heap) using
the delete keyword.
If your class declaration does not explicitly provide a destructor, the
compiler provides a "do nothing", default destructor function.
virtual Destructors:
It is important that a base class provide a virtual destructor. In that
way, the derived classes can override the virtual destructor with an
appropriate destructor, which will execute before the base class
destructor – otherwise only the base class destructor executes, which is not
going to be able to properly delete derived objects and return their memory to
the heap. The order of execution for destructors in the case of multiple
inheritance is the reverse of the order of execution of the constructors.
Friend Functions
The prototype of the friend function is placed inside of the class
declaration using the keyword friend. [A class determines who its friend
functions are – the functions themselves don’t.]
class Point {
…
…
public:
friend int compare(point &a, point &b): //The friend function is NOT in the scope
of the Point class.
…
};
Implementation of “compare” outside of class Point:
int compare(point &a, point &b)
{
… //This friend
function cannot access member data of Point directly – only through Point
objects.
return ….;
}
There are also two other ways of making friends of member functions in
other classes. You can call out the member function as a friend, as above, but
also use the full name of the member function with its class and scope
resolution operator. You can also make the another class a friend class by
declaring it in your class, as follows:
class Point {
public:
friend MumbleFratz;
//Now all member functions of MumbleFratz are friend functions of Point
…
};
Glossary
Arguments – (also known as “dummy
arguments” - the data passed as a list of one or more elements to a
function by its calling program. The argument list must conform to the
parameter list in the function's prototype.
Contrast with "Parameters"!
Assignment – An assignment statement assigns the
value returned by an expression to a
variable.
Attributes – each data item has five attributes
associated with it, as given in this table:
Data type
|
Determines how the data is encoded and operated on
|
Storage class
|
Determines where the data is stored
|
Duration
|
Determines how long the data exists
|
Linkage
|
Determines the accessibility of data in multiple-file
programs
|
Scope
|
Determines the visibility of data
|
Declaration - The program statement that associates an
identifier with what it identifies. A declaration can declare a variable,
specify the format of a structure, declare an external variable, or declare a
function or subroutine's return value and parameter list. A declaration may or
may not reserve memory. To declare a user defined data type is to provide the
plan, or specification, for the data type.
Declarations are an abstraction. To declare a function is to provide its
prototype, i.e., its identifier, return type and parameter list.
Default – Depending upon the context,
“default” can mean “the function having no parameters”, or it can mean “the
item or value that is provided by default” – i.e., in lieu of a provided item
or value.
Default Constructor – a “default” constructor is a constructor with
no arguments.
Definition - The program statement that defines the
existence of a variable or function. A definition reserves memory for the item.
The definition sometimes doubles as the item's declaration. To define a variable or an instance of a
class is to assign a value and store it in memory. Using a variable that has
been declared but not yet defined causes a compiler error. To define a function is to provide or
implement the code for the function in accordance with its prototype.
Dummy arguments – a term used to refer to the
arguments passed to a function by the calling routine. .
Duration – the life of the variable in regards to how
long it has real physical memory attached to it. The three categories of
“duration” are as follows:
Static
|
Data segment
|
During the entire program
|
Automatic
|
Stack or register
|
During a function call
|
Dynamic
|
Heap
|
User controlled
|
Expression – a combination of constants, variables, function
calls, and operators, which, when evaluated, returns a value.
Inline and the keyword inline – A member function that is implemented
directly in the class declaration can
be considered to be “ inline”, just as if it had the inline
keyword. If it is coded outside of the class declaration, then it can still be
made “inline” by use of the inline keyword. A member function that is
coded outside of the class declaration
and does not use the keyword inline is not “inline”. An
“inline” function is a function that is
considered by the compiler for direct incorporation of its body into the code
wherein it is called. There is no guarantee that the compiler will “inline” the
function, but it will generally do so when possible ( see also Horton,
page 287).
Linkage – an attribute of both data and functions that
is related to their visibility or accessibility in programs that are made up of
multiple translation units. The two
types of linkage are static (also known as internal) linkage and external
linkage. The linkage for an identifier is specified using the static and extern keywords. Identifiers with static linkage are
accessible in the modules (translation units)
in which they are declared. Statically linked variables and functions do not
appear in the object files created by the compiler, thus the linker cannot see
them. Variables and function declared with external linkage are in the object
files, and are visible to the linker. Thus, many modules can share an
externally linked identifier.
Parameters - The list of one or more data elements
seen by the called function in its "parameter list". Parameters are declared in the function's
prototype. Contrast with "arguments" or “dummy
arguments”.
Scope – the visibility or accessibility attribute of data
and functions - there are five scopes in C++, as shown in the following table:
Block
|
The area between the curly braces in a function
|
Function
|
Applies only to statement labels inside functions
|
Function prototype
|
The dummy arguments inside a function prototype
|
File
|
The areas outside function and class declarations
|
Class
|
Inside the curly braces of a class declaration
|
Translation Unit – an independently compiled
program module consisting of a C++ source code file and any other source code
files the program includes.
Type-safe Linkage – the C++ compilers and link editors
perform type-safe linkage by checking argument types when linking a function
call to the code for the function. See also name mangling.
Use - to use a variable is to include it on the right side
of an assignment statement or to include it anywhere in a conditional clause.
"Use" implies "read" - not write.
Initializers
There are two formats for initializing variables as part of the
declaration, as follows:
int amount = 3;
char ch = ‘A’; float val =
1.23; //etc.
int amount(3);
char ch(‘A’); float
val(1.23); // unique to C++
If the declaration is for a local static variable, then it will be initialized
the first time but never again.
Initializer Lists are used for initializing
structures and arrays. For example:
int numbers[3] = {1, 2, 3};
There are rules for determining when you can use an initializer list to
initialize an object, as follows:
·
The object can’t have private members
·
The object can’t have constructors
·
The object can’t have virtual functions
·
The object can’t be from a derived class
·
If you’re trying to create and initialize an array of
objects that have at least one of the four properties listed above, then you
can’t use an initializer list unless the objects have a constructor. In that
case, you can initialized each object in the array by calling a constructor in
the initializer list. See Flamig, page 189, and also see Constructor Functions and Constructor Functions - default values.
Inline Functions
An “inline” function is a function that is
defined (implemented) within the class declaration, or by means of the inline keyword.
Two notations exist for defining (implementing) inline functions for a
class. In the first, you code the body of the function directly into the class
declaration - i.e., within the class declaration curly brackets. The second
notation uses the inline keyword outside
the class. When using the second method, the implementing code should be
included in a header file, along with the class declaration. In this way, the
code will be visible to all code that uses the class.
Standard C++ Keywords
asm
|
const_cast
|
explicit
|
int
|
register
|
switch
|
union
|
auto
|
continue
|
extern
|
long
|
reinterpret_cast
|
template
|
unsigned
|
bool
|
default
|
false
|
mutable
|
return
|
this
|
using
|
break
|
delete
|
float
|
|
short
|
throw
|
virtual
|
case
|
do
|
for
|
new
|
signed
|
true
|
void
|
catch
|
double
|
friend
|
operator
|
sizeof
|
try
|
volatile
|
char
|
dynamic_cast
|
goto
|
private
|
static
|
typedef
|
wchar_t
|
class
|
else
|
if
|
protected
|
static_cast
|
typeid
|
while
|
const
|
enum
|
inline
|
public
|
struct
|
typename
|
|
Member Functions of a Class
Member functions are the functions you declare within the class definition.
You must provide code for these functions (implementation). Member functions
are named with the class name, followed by the :: operator, followed by the
function name.
Calling Member Functions:
A program calls a member function by using a period operator, as shown
here:
int vol = thisbox.volume(); //Where
"thisbox" is the name of a specific instance of the class and
volume() is the member method of that same class.
static Member Functions:
You would declare a member function static when it is intended to be used
only to access static data members rather than the data members of any
particular instances of that class. Static member functions can be defined
inline or outside the class as with any member function. A static member
function cannot access any non-static data members nor can it call any
non-static member functions. Since static member functions have no access to
non-static member data, they have no need for the this pointer and cannot use
it.
const Member Functions:
You would declare a member function const when objects of the class are to
be declared const at instantiation. [An object might be declared const because
you do not want its data members to be modified.] A const member function cannot call a non-const
member function of the same class, since this would potentially modify the
object. A member function is declared constant by appending the keyword const
to the function header (Horton, page 308). For example:
class Date {
void display( )
const;
//…
};
void Date::display(
) const { }
Member Conversion Functions:
A Member Conversion Function converts an object of the class in which it is
installed to an object of another type. The declaration of a member conversion
function is as follows:
operator target_conversion_type(); //keyword “operator”
followed by the “type” returned by the function.
The member conversion function is defined as follows:
Classname::operator
target_conversion_type()
{
//This
function starts with an object of type “Classname” and returns an object of
class “target_conversion_type”
}
The keyword const may appear directly after the parens in both
declaration and definition in order to guarantee that the original object is
not modified by the function.
Context in which Member Conversion Function would be used:
Class Date {
operator
long();
};
Date today(2.12,98);
long sum = 123 + today; //Compiler makes use of the member
conversion function to convert “today” to long.
Also:
DayOfYear rtndate()
{
Date dt
(10,11,88);
return
dt; //Compiler would use the
conversion function to convert to a DayOfYear object.
}
Two Way Conversions:
A class may contain both a “conversion constructor” and a “member
conversion function” which implement 2-way conversions between that class and
another class. However, note that there cannot be a “conversion constructor” in
ClassA and also a “member conversion function” in ClassB, both of which produce
an object of type ClassA, because the compiler would not know which to call.
virtual Member Functions:
The virtual qualifier on a member function is used to support polymorphism
by subsequent derivation from a class. Member functions may use the virtual
qualifier to indicate that the function is expected to be overridden in the
derived class by a function with the same name and parameter list.
[Question: If the base class is not abstract, and if the derived class does
not override the virtual member function, will there be a compile error? The
answer is no. See example programs, 20-1 and 20-2 in Stevens.
In those examples, the base class is not abstract, and its virtual function,
foo( ), is not overridden in the derived classes.]
If a virtual member function
prototype ends with “ = 0; “, that signifies a pure virtual function and also means that the base class is
an abstract base class, which
means the designer intends the class to be used only as a base class.
Furthermore, the virtual member function must
be overridden in any derived class that
uses it. A consequence of a class being abstract is that objects cannot be
instantiated from that class.
Multiple Inheritance
You specify more than one base class when you define a derived class with
multiple inheritance. The following
code shows how you define the CompanyCar class given that the Vehicle
and Asset class definitions are in scope:
class CompanyCar
: public Vehicle, public Asset {
//…
};
Overriding Members with Multiple Inheritance:
Member functions in the derived class that override the virtual functions
of the base classes may call the base class function using the scope resolution
operator as follows:
void DerivedClass : : functionX ( )
{
BaseClass1 :: functionX();
BaseClass2 :: functionX();
//If the function does not exist, the compiler searches up the hierarchy
until a matching function is found.
}
Ambiguity Due to Missing Function:
When the derived class has not overridden a virtual function of the base
classes, then the client program must use the scope resolution operator and
call a base class function as follows:
LeaseCar myChevy;
MyChevy.Vehicle::Display();
Or ,
MyChevy.Expense::Display();
Ambiguous Data Members:
When two or more of the base classes have identically named data members
and the derived class does not have such a data member, then the member
functions of the derived class must use the scope resolution operator to
resolve which data member to use, as follows:
LeaseCar myChevy;
…. Vehicle :: ctlno
….. ;
Or ,
….. Expense :: cltno …. ;
The preferred method of resolving ambiguities is to design the base classes
with protected data members
and use dedicated member functions in the derived class to access the specific
base class data members.
Operators
Arithmetic Operators
|
+
|
Unary plus
|
Does nothing
|
-
|
Unary minus
|
int y = -x;
|
*
|
multiplication
|
Prod = x * y;
|
/
|
Division
|
Byteoffset = bitno / 8; //offset to the byte
|
%
|
Modulus
|
Bitoffset = bitno % 8;
//bit offset in the byte
|
+
|
Addition
|
x = y + z;
|
-
|
Subtraction
|
z = x – y;
|
--counter;
|
Decrement (prefix)
|
Changes the variable before it contributes to the
expr.
|
counter--;
|
Decrement (postfix)
|
Changes the variable after it contributes to the
expr.
|
--counter;
|
Increment (prefix)
|
Changes the variable before it contributes to the
expr.
|
counter++;
|
Increment (postfix)
|
Changes the variable after it contributes to the
expr.
|
Relational Operators
|
>
|
Greater than
|
|
<
|
Less than
|
|
>=
|
Greater than or equal to
|
|
<=
|
Less than or equal to
|
|
==
|
Equal to
|
See note below
|
!=
|
Not equal to
|
|
Operators for Members, Pointers and Scope Resolution
|
::
|
Scope resolution operator
|
|
.
|
Period operator
|
Class Member Operator
|
->
|
Indirect Member Select Operator
|
Pointer to Member Operator
|
Logical Operators
|
&&
|
Logical AND
|
|
||
|
Logical OR
|
|
!
|
Unary NOT
|
|
Bitwise Logical Operators
|
&
|
Bitwise AND
|
|
|
|
Bitwise OR
|
|
^
|
Bitwise eXclusive OR
|
|
~
|
One’s complement
|
|
Bitwise Shift Operators
|
<<
|
Left shift
|
|
>>
|
Right shift
|
|
Compound Assignment Operators
|
+=
|
Addition assignment
|
|
-=
|
Subtraction assignment
|
|
*=
|
Multiplication assignment
|
|
/=
|
Division assignment
|
|
%=
|
Modulus assignment
|
|
<<=
|
Shift left assignment
|
|
>=
|
Shift right assignment
|
|
&=
|
Bitwise AND assignment
|
|
|=
|
Bitwise OR assignment
|
|
^=
|
Bitwise eXclusive OR assignment
|
|
Miscellaneous Operators
|
,
|
Comma operator
|
Val = (amt++, -tot, cnt+3); //Val=rightmost expression
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note on assignment (=) vs. equality (==)
The following is a valid statement but may not be what the programmer
intended:
If ( amount = 123) //…..
The variable amount will be assigned the value 123. Since 123
converts to boolean true, the if condition is satisfied. The programmer
probably did not mean to assign a value to amount. If not, he programmer
should have used the (==) equality operator.
Operator Precedence and Order of Evaluation
Op
|
Name
|
Pr
|
As
|
Ar
|
Ov
|
Example
|
::
|
Global Scope Resolution
|
17
|
R
|
1
|
N
|
::x
|
::
|
Class Scope Resolution
|
17
|
L
|
2
|
N
|
X::x
|
.
|
Direct Member selection
|
16
|
L
|
2
|
N
|
s.len
|
->
|
Indirect Member selection
|
16
|
L
|
2
|
Y
|
p->len
|
[ ]
|
Subscript
|
16
|
L
|
2
|
Y
|
a[i]
|
( )
|
Function Call
|
16
|
L
|
N/A
|
Y
|
rand()
|
( )
|
Type construction
|
16
|
L
|
N/A
|
Y
|
int(ch)
|
++
|
Post-increment
|
16
|
R
|
1
|
Y
|
n++
|
- -
|
Post-decrement
|
16
|
R
|
1
|
Y
|
n- -
|
Sizeof
|
Size of object or type
|
15
|
R
|
1
|
N
|
sizeof(a)
|
++
|
Pre-increment
|
15
|
R
|
1
|
Y
|
++n
|
- -
|
Pre-decrement
|
15
|
R
|
1
|
Y
|
- -n
|
~
|
Bitwise complement
|
15
|
R
|
1
|
Y
|
~s
|
!
|
Logical NOT
|
15
|
R
|
1
|
Y
|
!p
|
+
|
l plus
|
15
|
R
|
1
|
Y
|
+n
|
-
|
l minus
|
15
|
R
|
1
|
Y
|
-n
|
*
|
Dereference
|
15
|
R
|
1
|
Y
|
*p
|
&
|
Address
|
15
|
R
|
1
|
Y
|
&x
|
New
|
Allocation
|
15
|
R
|
1
|
Y
|
new p
|
Delete
|
Deallocation
|
15
|
R
|
1
|
Y
|
delete p
|
( )
|
Type conversion
|
15
|
R
|
2
|
Y
|
int (ch)
|
. *
|
Direct member selection
|
14
|
L
|
2
|
N
|
x.*q
|
->*
|
Indirect member selection
|
14
|
L
|
2
|
Y
|
p->q
|
*
|
Multiplication
|
13
|
L
|
2
|
Y
|
m*n
|
/
|
Division
|
13
|
L
|
2
|
Y
|
m/n
|
%
|
Remainder
|
13
|
L
|
2
|
Y
|
m% n
|
+
|
Addition
|
12
|
L
|
2
|
Y
|
M + n
|
-
|
Subtraction
|
12
|
L
|
2
|
Y
|
m – n
|
<<
|
Bit shift left
|
11
|
L
|
2
|
Y
|
cout << n
|
>>
|
Bit shift right
|
11
|
L
|
2
|
Y
|
cin >> n
|
<
|
Less than
|
10
|
L
|
2
|
Y
|
x < y
|
<=
|
Less than or equal to
|
10
|
L
|
2
|
Y
|
x <= y
|
>
|
Greater than
|
10
|
L
|
2
|
Y
|
x > y
|
>=
|
Greater than or equal to
|
10
|
L
|
2
|
Y
|
x >= y
|
==
|
Equal to
|
9
|
L
|
2
|
Y
|
x == y
|
!=
|
Not equal to
|
9
|
L
|
2
|
Y
|
x != y
|
&
|
Bitwise AND
|
8
|
L
|
2
|
Y
|
s&t
|
^
|
Bitwise XOR
|
7
|
L
|
2
|
Y
|
s^t
|
|
|
Bitwise OR
|
6
|
L
|
2
|
Y
|
s|t
|
&&
|
Logical AND
|
5
|
L
|
2
|
Y
|
u && v
|
||
|
Logical OR
|
4
|
L
|
2
|
Y
|
u || v
|
?:
|
Conditional expression
|
3
|
L
|
2
|
N
|
u ? x:y
|
=
|
Assignment
|
2
|
R
|
2
|
Y
|
n = 22
|
+=
|
Addition Assignment
|
2
|
R
|
2
|
Y
|
n += 8
|
-=
|
Subtraction assignement
|
2
|
R
|
2
|
Y
|
n -= 4
|
*=
|
Multiplication assignment
|
2
|
R
|
2
|
Y
|
n *= -1
|
/=
|
Division assignment
|
2
|
R
|
2
|
Y
|
n /= 10
|
%=
|
Remainder assignment
|
2
|
R
|
2
|
Y
|
n %= 10
|
&=
|
Bitwise AND assignment
|
2
|
R
|
2
|
Y
|
s &= mask
|
^=
|
Bitwise XOR assignment
|
2
|
R
|
2
|
Y
|
s ^= mask
|
|=
|
Bitwise OR assignment
|
2
|
R
|
2
|
Y
|
s |= mask
|
<<=
|
Bit shift left assignment
|
2
|
R
|
2
|
Y
|
s <<= 1
|
>>=
|
Bit shift right assignment
|
2
|
R
|
2
|
Y
|
s >>= 1
|
,
|
comma
|
0
|
L
|
2
|
Y
|
++m, - -n
|
Other authors present similar but different tables of precedence. See Liberty, inside
back cover.
Overloading
Overloading Class new
Operator:
void* operator new (size_t)
{
//Whatever
code is needed to manage memory allocation
}
The argument to new is a type defined by the system that specifies memory
sizes. I have not seen this explained anywhere, but it is logical that the
compiler would have to provide the size of the memory required by the object
being created and that the allocation would be dependent upon the class. Whether the overloaded operator makes use of
that parameter is irrelevant – it must be shown as above.
Overloading Class delete
Operator:
void operator
delete(void* p)
{
//Whatever
code is needed to return the allocated memory to the free store
}
You must provide an overloaded delete
operator, even if it does nothing – otherwise, the compiler calls the default delete
operator function when you use the delete
operator, which assumes the function’s argument points to a buffer allocated
from the heap. The results would be undefined and unpredictable .
Overloading Class new [ ]
Operator:
The overloaded Class new
and delete operators cannot
be used when instantiating and deleting arrays of the Class objects.
Declarations for overloading the new [ ]
and delete [ ] should be as shown in the following examples:
void* operator new [ ] (size_t size)
{
//Whatever
code is needed to manage memory allocation, including maintaining a list of the
allocated memory to be returned.
}
For example, the argument size
might be used in keeping track of what amounts of memory have been allocated as
well as a record of the pointer to that memory.
Overloading Class delete
[ ] Operator:
void operator delete
[ ] (void* p)
{
//Whatever
code is needed to return the allocated memory to the free store, based on
information maintained by the overloaded
new [ ] operator
}
Overloaded Assignment Operator:
Unless you overload the assignment operator, the compiler does a simple
member by member assignment, which could lead to serious complications if the
class allocates resources in the constructor.
Class MumbleFratz {
MumbleFratz&
operator=(const MumbleFratz&);
//Parameter in the parens refers to the object being assigned from
– that is, the object that would be on the right side of the assignment.
};
Consider the following
code:
Date birthday(3,13,30);
Date newday;
newday = birthday;
The overloaded assignment operator function sees two parameters – the first is implied and is the object for
which the function is being called. In this case the function is being called
for the object on the left side of the assignment: the newday object. The
second parameter is being supplied as an argument and is the object on the
right side of the assignment, in this case the birthday object. The
assignment operator function will return a reference
to the object that received the assignment (return *this;), making possible
the following statement:
oldday = newday = birthday;
Class Member Operator ( . ) Functions:
Binary Arithmetic Operators can be overloaded, which will permit an
expression like this:
olddate = olddate + 21;
They can also be used explicitly, as follows:
olddate.operator + (21);
Relational operators, auto-increment and decrement operators, and unary –
operator can all be overloaded. The subscript operator, [ ], can be overloaded
to provide subscripted access to a string value, as follows:
class String {
//…
char&
operator [ ] (int n);
const
char& operator [ ] (int n) const;
};
Because the operator [ ] function
returns a non-const reference to the character being subscripted, the
program can use the expression on the left side of an assignment, like this:
String string1(“The Ides of March”);
String1[4] = ‘x’;
//which changes the string to “The xdes of March”
Pointer-to-Memory Operator (->):
The pointer-to-memory operator can be overloaded to become a smart pointer
– that is, to ensure that a pointer to a class object always has a value. It
must be implemented as a non-static member function.
Overloading the << and >> Operators:
Instead of writing functions that use std::cout and std::cin for output and
input from the console, it makes more sense to modularize the display functions
as follows:
void Classname::Display(std::ostream& os) const
{
//….
os
<< “text string” <<
variable << … ;
}
And then it might be used as follows:
object_name.Display(std::cout); or object_name.Display(std::cerr);, etc., depending on which device it should
go to.
Constructor Parameter
Initialization Lists
The colon operator ( : ) is used to specify a parameter initialization
list.
Classname(param list) :
const_datamember1(param1), const_datamember2(param2), … { }
//for an inline constructor
Classname::classname(param1, param1, param2,… ) :
const_datamember1(param1), const_datamember2(param2), …
{
} //for a
constructor not inline.
To initialize references:
class DateTime {
const Date& dt; //reference
to Date
const Time& tm; //reference
to Time
public:
DateTime (const Date& d, const Time& t) : dt(d),
tm(t) { }
};
Or, if the constructor is not inline:
DateTime :: DateTime (const Date& d, const Time& t)
: dt(d), tm(t)
{
}
When the above constructor is invoked, the arguments, reference to a date
and reference to a time are used to initialize the const member variables.
From that point on, the object of class DateTime will be immutable (cannot be
changed).
One way to initialize const data members:
class Date {
const int
months;
public:
Date() :
months(12) { } //Here the variable months is initialized to 12
};
To initialize the member objects of classes not having default constructors:
class Date {
int da, mo,
yr;
public:
Date (int
d, int m, int y) { da = d; mo = m; yr =
y; }
};
class DateTime {
Date
dt; //A Date object called “dt” – how
is it to be initialized without an assignment operator ?
public:
DateTime(
int d, int m, int y) : dt(d, m, y) { }
};
Note that the above method permits the Date object, dt, to be
initialized without using an assignment operator.
The parameter initialization list can be used to initialize all class
object data members. Consider the following code:
class Employee {
int empno;
Date
datehired; //Note that a Date object is instantiated assuming a default (no
parameters) constructor.
public:
Employee(int
en, Date& dh);
};
//First method of implementing the constructor for Employee
Employee::Employee(int en, Date& dh)
{
empno = en;
datehired = dh;
//Note that this assumes the Date class provides support for assignment
operator.
}
//Second method of implementing the constructor for Employee
(using the parameter initialization list).
Employee::Employee(int en, Date& dh) : empno( en), datehired (dh) { }
The first method requires Date
to have a default constructor and to support the assignment operator,
which not all classes do.
The second method constructs the object and initializes the data members,
eliminating the need for a Date default constructor and assignment operator.
See Stevens, page 214.
Order of Initialization:
point :: point () : y(x), x(0) //if x was declared before y, then x is initialized first (see Flamig, p 183)
{
return
}
Try not rely on the order that
member objects are initialized when using member initialization lists. If you
find yourself relying on that order, you might want to rethink the design of
your class.
Pointers
The this pointer:
The this pointer exists for a class whenever a non-static member
function is executing. It points to a specific object of the class at hand -
specifically, it points to the object for which the member function is executing. Whenever a member function is called by the
program for a specific object, then that member function is passed the this
pointer, and that pointer points to that specific object.
Every reference to any member of the class from within a member function of
that same class, implicitly uses the this
pointer. Nonetheless, you may also use the this pointer explicitly with
the member pointer operator, "->".
Static member functions have no access to non-static member data, and as a
consequence have no need for the this pointer and cannot use it.
Since the this pointer is available to a member function, it can be
passed by value to any other function in the program that needs the address of
the object, or it can be returned the caller of the function as a return value.
Points on pointers (from Stevens, page 121):
int i, j; //int
variables
int* ip; //pointer to
an int variable
ip = &i; //assign address of
int variable to pointer to int
j = *ip; //retrieve
int that int pointer points to (also called "de-referencing" ip)
int** ipp; //pointer to int pointer
ipp &ip; //assign address of
pointer - now ipp holds the address of ip, which is the pointer to int
j = **ipp //retrieve int through
pointer to int pointer (also called "de-referencing the result of
de-referencing the pointer to the pointer)
Pointers to const: Example – const char*
str; //declaration of a pointer that points to a const. Such a pointer cannot
be used to modify the const variable.
const Pointer Variables: Example – char* const ptr = buf;
//declaration of a pointer that is, itself, const and points to a char. Such a
pointer cannot be modified, but does not affect whether or not the char that it
points to can be modified through the pointer.
void Pointers: Example
– void* vptr; //useful for overriding
the operator new.
The spaces are ignored by the compiler, but the style shown is favored by
C++ programmers.
Private Classes
Private classes are classes with private constructors. Only members and friends can instantiate objects from a private
class.
Qualifiers
const:
When applied to data, the const
qualifier prohibits re-assignment from the initialized value. When applied to
functions, the const qualifier prohibits the function from modifying any
data. C programmers might be more
likely to use the #define to create a symbol with a value. Even though a const variable seems to
be an oxymoron, that is only so because we use the term variable when we
probably need a better word to describe the stored data entities. An object
might be declared const because you do not want its data members to be
modified - i.e., you want them to be as if they were constants themselves.
volatile:
Volatile is a warning to the compiler that the program may be changing the
variable in unseen ways. It tells the compiler not to make optimizations on the
variable.
Consider the ramifications of multi-threaded programs or interrupt service
routines that involve asynchronous execution paths through the code.
Reference Data Members
Reference data members must be initialized with the constructor's parameter
initialization list - see above!
Storage Classes
auto:
Local variables are auto by default. Use of the keyword is optional. Function parameters are auto by default
unless they are declared to be register. Auto
variables are also known as "stack" variables, because they
are kept on the stack (as are function arguments) instead of main memory.
static:
For local variables, it means a scope limited to the block that it is in.
The static class is different from auto in that it retains its value between
executions of the statement block. Any initializers are effective on the first
execution only. For variables outside
of any statement block or function, the scope is the entire file. An additional
implication of static as a storage class is that it is invisible to the linker –
i.e., it cannot be referenced from outside of its translation unit. Stevens, page 94.
extern:
Declares external variables that haven't yet been defined. Typically, those
variables are located in another translation unit.
There may be several declarations of an extern variable. The location where the
declaration occurs determines the scope. If it is made within a function, then
it can only be referenced from within that function. If it appears outside of
any function, then it can be referenced from any function that follows the declaration
in that translation unit.
register:
Same as auto except that the compiler is being allowed to put the variable
into a register (if it chooses to). It is not appropriate to take the address
of a register variable, since a register has no address.
Templates
Specification of a Class Template:
template <class T> //The ”T” identifier represents the parameterized data type
throughout the definition.
class LinkedList
{ //”LinkedList” is the
generic class that will be used as the container for LinkedList objects that
can be any one of several different data types.
T& p; //Declares a reference to the parametized data type
called “p”
//…
public:
//…
void AddEntry(T
&entry); //Member function
AddEntry takes a reference to the parametized data type and adds it to the
linked list.
};
When the AddEntry function is implemented, it too must be identified with
the template, as follows:
template <class T>
void LinkedList<T> :: AddEntry(T &entry)
{
//…
}
Both the implemented member function(s) and the specification of the
template should be placed in a header file and made visible to any program that
will declare objects of that class, as follows:
LinkedList<int>
IntList; //Where the data type
inside the “< > “ specifies the data type which replaces the parameter “T” in the a above sample
specification, and the name IntList is the name of the object being
instantiated from the class LinkedList. Note that the object being created is
not an “int” – rather it is a linked-list of “int”, albeit an
empty list.
Note that the parametized data type in the “< >” can also be a list
of different data types – i.e., you are not limited to one argument when you
design a template.
Function Templates:
A template can define a parameterized non-member function, as follows:
template <class T>
T& min(T& a, T& b)
{
return (a
< b ? a : b);
}
Templates and Abstraction: See Stevens, pages 350-1.
Text References:
Al Stevens, Teach Yourself C++, 6th Edition, IDG Books,
ISBN 0-7645-4634-1
Ivor Horton, Beginning Visual C++ 6, WROX Press, ISBN
1-861000-88-X
Jesse Liberty, Sams Teach Yourself C++ in 21 Days,
Third Edition, Sams Publishing, ISBN 0-672-31515-7
Bryan Flamig, Turbo C++, A Self Teaching Guide, Wiley, ISBN 0-471-52903-6
TypeCasting and Type Conversions
Rules for conversions between numeric types (char, int, long integers, and
float):
“Up” casts are made
without any loss of information.
“Down” casts may cause a loss of information, and
the compiler should give a warning to that effect.
If a “down” cast is made using typecasts, there is
no difference in whether or not there will be truncation, only that the
compiler now assumes that you know what you are doing and does not give a
warning.
.
Virtual Base Classes
Rules for Virtual Base Classes:
A class with a constructor and parameters cannot
be a virtual base class.
A virtual base class will be declared as virtual
in the declarations of its derived classes.
A pointer to a virtual base class cannot be cast
to a class that is descended from it.