C++ Workshop II


The next chapter of the C++ book gives a whirlwind tour of the basics

of the C++ syntax that are necessary for object oriented programming.

This section is almost like jumping into the pool and hope you get

acclimated to the water. C++ is not a good language for a beginner,

and this section makes it clear why. To really get a handle on this

section without prior programing experience is a very tall order.


In parsing this chapter, keep a few things in mind. Much of the code

snippets given as an example in this section would be written outside of

your main program, as library files and header files. While we discussed

the preprocessor directive previously in both the C and C++ workshops,

we hadn't looked at them in detail. For those not familiar with these

construction, which are core to C programing, a few words are in order

about them.


There are two different external file types that your compiler uses to

build your program, aside from the file that your main program code is

stored in. They are called header files or libraries. Most normally,

there is a header file for each library. They also share part of their

name according to a naming convention. Standardized libraries, such

as iostream have header files. The libraries can be static or shared.

Either type is unlinked C bitcode, or objects files in C language

parlance. A group of object files archived together, usually with the

'ar' program, form these libraries. Thee files normally exist in

/usr/lib or /lib and might have a suffix of '.so'. It is sometimes

necessary to compile a program with the -I option to tell the compiler

to include a library, even though the preprocessor include line is

designating the header file. Usually if the header file is math.h then

the library is libmath.


Declaration of variable types, function names, and preprocessor constants

which apply to the library files are header files. These are what

is referred to within your program, such as <iostream> or <cassert>.

In C these are .h files such as /usr/include/strings.h. In C++ they

might not, as in /usr/include/g++/iostream. Take a peek and look at

some of your header files. Becoming familiar with their syntax will

greatly improve your hacking skills.



The reason to discuss this, and we should really talk even more about

this, is that the examples in the C++ text implicitly and explicitly

imply that the code being written is for library objects, and written in

external files. When we discuss making classes, the most logical place to

make a new class is in an external file, compiled to an C object for use

later by someone else's program. Calling class objects is not a problem.

A simple explanation of the “new” keyword and type defining the class

structures puts you on your way. But once we discuss Constructors,

Destructors, data hiding, public interface design, private data and

implementation design, operator overloading, and function overloading

and polymorphism, then we are talking writing modules for others to use,

or even for us to use concurrently to the main code we are writing.


Section 2.1 is a big section which walks through much of the C++ basic

design, and I will go over it piece by piece over the next couple of days.




In organizing the concepts of C++ which are being introduced we can view each of them separately, but studying them it is essential to keep in mind that they are designed to function together in the construction of programs which are easier to debug, which allow for cleaner overall syntax, and which encourage creation of reusable code.


We shall explore:


Data Types (build in and user created)


Pointers and References (and their subtle deference)


Manual Memory Allocation and the 'new' and delete keywords


Class Declaration


Class Definition


Private data


Public data


Object instantiation and access, Class typedef


Class Constructors


Class Destructor


Function or Method Overloading


Operator Overloading


External extension of definitions


Copy Constructors


at the end we'll try to actually make the example class, which is an extended array.



1) Data Types: DATA DATA DATA, everything is DATA


As we know, C and C++ have built in data type which each variable and object the language needs to be defined as. Both C and C++ are typed language.


We have char, int, double, float, pointer, and so on, a complete list of which is available around the net. A pointer stores an address of another C++ object. Different hardware and software platforms also have different sizes that it allocates for these data types, creating an inconsistency which, as an aside, the Free Software community has tried to address with the creation of the glib library, part of the GTK project.


What is less apparent is that with regard to a computer, everything is data. We have the data that we create and manipulated with instructions, but the instructions themselves are a form data which we can package up and save for later use, and feed to the CPU at will. When the CPU runs out instructions the resulting actions are further instructions that can be packaged and saved as data. In a word, everything is data, and how we package that data is what separates one programming language from the next.


This concept is covered extensively in NYLXS “Introduction Programming with Perl” class and since this is an advanced topic, we won't get much further into this. But we will review the basics of data types and then look at the new feature that C++ gives which C didn't have in such a generous way, the ability to easily create new data types easily and in a reusable fashion.


An integer in the C family on the 32 bit Intel clone architecture is defined as a marked space in memory of 32 bits in size to represent both positive and negitive numbers. On the new 64 bit architecture that many of you might have, I don't know if this still holds true since the word size of a those machines is 64 bits or 8 bytes.


when you use the declaration


int a = 3456;


The computer your program sets aside 32 bits, 4 bytes, of space in ram and puts the binary representation of that value in that space. The leftmost bit is usually the signed bit determining whether the number represented within is either positive or negative. A signed integer can therefor have a maximum value of 2,147,483,647 positive or negative. beyond that you must use a long int, which on 32 bit architecture actually won't help you, or to use external libraries with other data types defined.


By default, C allows certain syntax with a data type. It will automatically translate it, for example, into a char that will print its representation for functions such as printf or in C++ the cout object:


printf (“%d\n”, myint);

cout << myint;


It can be combined with operators

it can be used with the assignment operator to fill or initialize its space with data.


int myans,myx = 6,myy =12;


It can be combined with arithmetic operators and have results assigned accordingly.


myans = myy + myx;


Two of them can be compared.


while (myy < myx){

...

...

}


They can be auto incremented


myy++;

++myy;


and so on....


One data type is actually a serial arraignment of data, that is an array.


int myarray[100];


This defines an array of 100 elements indexed from zero to one hundred.


myy = myarray[4];


assigns the fifth element of our array to our integer variable myy. One thing you can not do with an array data type is use an assignment operator on the entire array object.


myarray[] = myarray2[]; //THIS IS AN ERROR


C++ allows up to define our own data types that have all the properties of the built in ones. It uses the class mechanize, operator overloading, and the “new” keyword to accomplish this.



2) Pointers and References – Where did I PUT THAT!


When we create data for our program, we ask the program to insert memory

into RAM and to retrieve or assign the data from that memory location

for use. Internally the program keeps track of the symbols and the

memory locations. In fact is you run the program “nm” on a C or C++

binary it will tell you all the symbols that it has in that binary.


ruben@www2:~/cplus> nm file3|less


0804a210 A __bss_start

08048a84 t call_gmon_start

0804a29c b completed.1

0804a0bc d __CTOR_END__

0804a0b4 d __CTOR_LIST__

U __cxa_atexit@@GLIBC_2.1.3

0804a204 D __data_start

0804a204 W data_start

08048ed0 t __do_global_ctors_aux

08048ab0 t __do_global_dtors_aux

0804a208 D __dso_handle

......



But we can also create memory locations that are assignable, and store

a representation of that memory location directly into a variable that

only stores the memory location as data, not the data itself. In c

and C++ this is called pointers and we can use the following syntax to

create them.


int *pt = &myint;


This declares the pointer to an int variable called pt which stores the

address for myint. The syntax int * in a declaration (and ONLY in a

declaration) says make a pointer to an int. The & syntax in front of a

variable myint says don't return the value of the variable, but return

the address of the data stored in the variable itself.


There are functions that return only pointer data. Those functions make

it possible to access memory without the declaration of variables at all.

There are also declarations that can be made in C and C++ which can

create variables without variable names either.





int (*pt)[10];


This declares a pointer (pt) to an array of 10 integers.


char (*p)[10][100]


This is a pointer which addresses an array of 10 pointers (implied)

to arrays of 100 chars each. Commonly this is know as a point to an

array of 10 strings. The symbolic variable name for an array often

gets automatically cast as a point type.


Most string functions in C return a char pointer for example


char * strtok(char *s1, const char *s2);


This would return an address of a char, which in theory would represent

an array of chars. In use it would look like this



char * spt;

char wd1[100] = 'hello world', wd2[100] = ' ';


spt = strtok(wd1,wd2);

printf(“%s\n”, spt);






3) Manual Memory Allocation and the 'new' and delete keywords


C++ makes it very convent to create dynamically allocated memory which

is accessed by pointers. We might call these anonymous pointers because

they do not point to any variables, just defined memory. We do this

with the key word “new”.


int *pt = new int(124);


This creates a new int pointer called pt and assigns to the memory

pointed to by pt with the integer value 124.


delete pt;



deletes the anonymous pointer pt.


int *pt = new int[100];


This declaration creates a new int pointer to an array of 100 integers,

with no data assigned yet to that block of memory.


delete [] pt;


deletes the entire array pointed to by pt and then undefines pt.





4) Class Declaration – Type Type Type


When using C++ one of the key reasons for choosing this language involves

our ability to roll our own data types. While with C programming we

can use typedef with unions and structures, the class system with C++ is

much ore extensive and flexible, giving genuine data type ability to the

programmer, a flexibility previously reserved for the elite programmers

who could hack the core C programming interface, probably involving a

bit of assembly to boot.


The first thing you need to do when making a class is to declare it,

similarly to how one needs to declare a function. Declaring the class

alerts the compiler of the new symbols that will be used in further code.

The declaration does not include the definition. that can be done later.

All of this most commonly takes place in an external file so that it

can be reused, or even a .h header file.


class IntArray;

once we make this declaration, we can declare objects of this class as

we could with any other data type


IntArray array_object;


or even declare pointers to it such as:


IntArray *pt_array_obj = new IntArray;


After declaring the class itself, we need to now declare the internals

of our class, or its body.


class IntArray{

public:

bool operator==(const IntArray&) const; //This is a declaration

bool operator=!(const IntArray&) const; //This is a declaration


bool operator=(const IntArray&); //This is a declaration

int size() const; //This is a declaration

void sort(); //This is a declaration

int min() const; //these are all declarations

int max() const;

int find() const;


private:

//nothing yet

}


All this is so that later we can use the IntArray as a data type


IntArray myarray;


and we can then use the IntArray objects as any other variable


myarray[] = a[];


Then we can use the

Class Definition dot syntax to gain access to the public interface


myint = myarray.size();


or if we use a pointer as follows


IntArray * parray = new IntArray;


myint = parray->size();




5) Class Definition:


Once we declare the class type by also declaring the internals and externals of a class, and by using the keywords 'class', 'public:' and 'private:' we need to define the class its qelf, or essentially code the class. All the methods that we declared now need to be coded, either either as 'inline' methods or normal methods. For the purposes of object oriented languages like C++, methods are a subclass of functions, like functions in every way accept scoped to class objects. Inline functions are quick jobs that can be simply defined and which the compiler can replace at the call point, reducing the number of times the code has to be loaded, interpreted and run in our program, all of which is overhead of normal function or method calls. Functions that are used to access basic data which is otherwise private in a class are ideally suited for inline functions. Methods defined in classes are automatically attempted by the compiler to be compiled inline.


Lets look at the example that the text is using to give us a tour of C++. The text is trying to create an object which wraps the common C type integer array in order to give it more flexibility and features. A common beginners error in C programming is the attempt to assign one array to another array with code simlar to this


a[] = b[]; //ERROR


This is particulary common when working with strings, which are char arrays


char quote[255] = “A society which can not archive, can not share digital materials can not remain politically Free in the digital era”; //legal as an assignment;


char quote2[255];


quote2 = quote; //ERROR


If we wrap arrays in a new class, creating a new data type, we might first declare it as above, preferably in a header file:


class IntArray;


Now we declare the classes members, defining the internals of the class. Unlike functions (but like C structs) we need to end the body of a class definition with a semi-colon.


We define both public and private components of our class. the public section is the API that we are building. The private area is the internal machination of the class. The inner machinery that the programmer using our class doesn't need to become confused with.





#ifndef TOP

#include <iostream>

#endif

#ifndef INTARRAY_H

#define INTARRAY_H

#endif


class IntArray{


public:

static

//constructors

explicit IntArray(int size = DefaultArraySize);

IntArray( int *array, int array_size);

IntArray(const IntArray &rhs);


//destructor


~IntArray(){ delete [] ia };


//operators

bool operator==(cont IntArray&);

bool operator!=(const IntArray&);


//assignment operator

IntArray operator=(const Intarray&);


int size() const;

void sort();

int min() const;

int max() const;

int find( int value) const;




private:

static const int DefaultArraySize = 255;

int _size;

int *ia;

}; //remember the colon



This defined Class has several declared methods, which can later be defined. The

purposes of these methods can be classified and we will be looking more closely at them in the coming sections.


6) Private Data


Much of the internal work your class does, and the data it keeps track of is the domain of the class objects, and which have what is commonly know as no consumer usable parts within. These internal segments of your object code are best hidden from the users space, and is safer for the user not to monkey with. This data is designated by the private tag, and this technique is called information hiding. In our example we have only two data items which are no accessible to the user directly, int _size and int * ia .


private:


int _size;

int *ia;


Our job is to do the accounting of out objects size and the int array which we are wrapping in our objects. So direct access doesn't make much sense. We access these data's in a variety of ways which we will build examples of, mostly through a well documented public interface.


7) Public data


The public interface is, at least in our example, more complicated. We will need accessory methods, methods to gain access to otherwise private data, and we will need constructors, destructors, operator overload methods, and copy method. We've declared a few of these already in our class definition, but we haven yet defined the methods themselves. Our public interface is the application interface we are building for our new data type. Special attention needs to be given to the planning of this interface if you expect wide adoption of the class.


At this point, a good example program is needed. While I'm not that masterful with language, and the texts are as clear as mud on many issues, the following has been put together in an effort to create a working sample C++ program for us to work with.


Our C++ program is in three files which are located on the NYLXS site and posted here for convince.



http://www.nylxs.com/docs/workshops/intarray.h.html


1  #ifndef INTARRAY_H
 2  #define INTARRAY_H
 3  #endif
 4
 5  using namespace std;
 6
 7  class IntArray;
 8
 9
10  class IntArray{
11
12          public:
13          //constructors
14          //explicit IntArray(int size = DefaultArraySize);
15          IntArray();
16          explicit IntArray(int);
17          IntArray( int *array, int array_size);
18          IntArray(const IntArray &rhs);
19
20          //destructor
21
22          ~IntArray(){ delete [] ia; };
23
24          //operators
25          bool operator==(const IntArray &);
26          bool operator!=(const IntArray &);
27          //assignment operator
28          IntArray& operator=(const IntArray &);
29          //index operator
30          int& operator[](int);
31
32          int size() const;
33          void sort();
34
35          int min() const;
36          int max() const;
37
38          int find( int value) const;
39
40
41
42          private:
43          static const int DefaultArraySize = 255;
44          int _size;
45          int *ia;
46  }; //remember the colon
47
48

This is the example header file


http://www.nylxs.com/docs/workshops/file4.C.html



 1  #ifndef TOP_H
  2  #define TOP_H
  3  #include <iostream>
  4  #include <stdlib.h>
  5  #endif
  6  #ifndef INTARRAY_H
  7  #include "intarray.h"
  8  #define INTARRAY_H
  9  #endif
 10  using namespace std;
 11  IntArray::
 12          IntArray(){
 13                  _size = DefaultArraySize;
 14                  int * tmp;
 15                  ia = new int[_size];
 16                  for (tmp = ia; tmp < (ia + _size); tmp++)
 17                          *tmp = 0;
 18          }
 19
 20  IntArray::
 21          IntArray( int sz ){
 22                  int * tmp;
 23                  if(sz < 0){
 24                     cerr << "Size must be greater than 0 and an integer\n";
 25                          exit (-1);
 26                  }
 27
 28                  _size = sz;
 29
 30                  ia = new int[sz];
 31                  for(tmp = ia; tmp < (ia + sz); tmp++)
 32                          *tmp = 0;
 33          }
 34
 35  IntArray::
 36          IntArray( int *tmp, int sz){
 37
 38                  if(sz < 0){
 39                          cerr << "Size must be greater than 0 and an integer\n";
 40                          exit(-1);
 41                  }
 42                  _size = sz;
 43                  int * tmp2;
 44                  ia = new int[sz];
 45                  for(tmp2 = ia; tmp2 < (ia + sz); tmp2++,tmp++){
 46                          *tmp2 = *tmp;//copy tmp to ia
 47                  }
 48          }
 49
 50  IntArray::
 51          IntArray(const IntArray &rhs){
 52                  _size = rhs.size();
 53                  ia = new int[_size];
 54                  for(int i = 0; i < _size; i++){
 55                          ia[i] = rhs.ia[i];
 56                  }
 57          }
 58
 59
 60          //operators
 61  bool
 62  IntArray::
 63          operator==(const IntArray &rh){
 64              bool test;
 65              int sz = size(), i;
 66              if (sz == rh.size())
 67                  test = true;
 68              else
 69                 return false;
 70
 71              for(i = 0; i < 0; i++){
 72                if (ia[i] == rh.ia[i])
 73                   test = true;
 74              else
 75                    return false;
 76              }
 77              return test;
 78           }
 79
 80
 81  int&
 82  IntArray::
 83          operator[](int index){
 84                  if (index < 0 || index > _size)
 85                          exit (-1);
 86                  return ia[index];
 87          }
 88  bool
 89  IntArray::
 90          operator!=(const IntArray &rh){
 91              bool test;
 92              int sz = size(), i;
 93              if (sz != rh.size())
 94                  return test = true;
 95
 96              for(i = 0; i < 0; i++){
 97                if (ia[i] != rh.ia[i])
 98                   return true;
 99              else
100                    test = false;
101              }
102          return test;
103          }
104
105          //assignment operator
106  IntArray&
107  IntArray::
108          operator=(const IntArray& rh){
109             int sz = rh.size(), *tmp, *tmp2;
110             delete [] ia;
111             int * ia = new int[sz];
112             tmp = ia;
113             for (tmp2 = rh.ia; tmp < (tmp + sz); tmp++){
114                  tmp2++;
115                  *tmp = *tmp2;
116             }
117             return *this;
118          }
119
120  inline int
121  IntArray::
122          size() const{
123                  return _size;
124          }
125
126  void
127  IntArray::
128          sort(){
129          //good ole bubble sort
130             int *sorted, last, *index;
131
132             for(index = ia; index < (ia + _size); index++){
133                for(sorted = index; sorted < (ia + _size); sorted++){
134                   last = *(sorted + 1);
135                   if (*sorted > last){
136                      //switch - pushing higher to back
137                      last = *sorted;
138                      *sorted = *(sorted + 1);
139                      *(sorted + 1) = last;
140                   }
141                }
142             }
143          }
144
145  int
146  IntArray::
147          min() const{
148             int low, *tmp = ia;
149             for(low = *tmp;tmp < (ia + _size); tmp++){
150                if (*tmp < low)
151                   low = *tmp;
152              }
153
154              return low;
155           }
156
157  int
158  IntArray::
159          max() const{
160             int high, *tmp = ia;
161             for(high = *tmp;tmp < (ia + _size); tmp++){
162                if (*tmp > high)
163                   high = *tmp;
164              }
165
166              return high;
167           }
168
169  int
170  IntArray::
171          find( int value) const{
172          int * tmp = ia;
173          for(;tmp < (ia + _size); tmp++){
174               if(value == *tmp)
175                  return value;
176             }
177             return (-1);
178          }
179
180
181
182
183
184


This is the example library file and this is the test program that uses our classes



http://www.nylxs.com/docs/workshops/file4_main.C.html


 1  #ifndef TOP_H
 2  #include <iostream>
 3  #define TOP_H
 4  #endif
 5
 6  #ifndef INTARRAY_H
 7  #include "intarray.h"
 8  #define INTARRAY_H
 9  #endif
10
11  int main(){
12     IntArray myarray1;
13     myarray1[2] = 400;
14     cout << myarray1[2] << _end;
15     return 1;
16     }
17
18