Bridging from VB to C++


By Ryanne Thomas Dolan


I usually prefer to begin at the beginning, starting with programming theory and math, but as I expect you already know a good deal about computers and how they tick, I will attempt to distill this tutorial into simply the C-specific aspects of programming. The goal here is to progress to a level at which we can do everything in C++ that we could do in VB.


Firstly, we might ask which purpose we serve by learning another language. VB contains more power than we are likely ever to tap, so it may seem ridiculous to dive into a harder language when what we have will surely suffice. However, once you sharpen your sword, it seems silly to return to a dull blade. Only by learning the language and seeing for ourselves the power it contains might we come to appreciate C++.


C++ is a high-level programming language, which means that most of the “nitty-gritty” operations are handled internally. Lower-level languages require increasingly many statements to produce the same effects. Assembly is the lowest-level human-readable programming language and requires virtually every instruction to be programmed manually using only about 100 processor-specific operation codes. C, a language invented by Dennis Ritchie in 1972, is also very low-level, but is certainly higher than assembly. All C programs can be built directly into assembly programs, which are then compiled into processor-specific machine code binaries. Thus, the C language can be used to target every conceivable processor and therefore is used extensively for embedded systems programming by computer engineers.


C++, invented by Bjarne Stroustrup, is the high-level, object-oriented adaptation of C. It was designed to facilitate object-oriented programming (OOP), a coding style we will discuss shortly. C++ programs can be built for most systems, but usually different targets require completely different compilers. For example, Microsoft offers eMbedded C++ as a free download for compiling PocketPC programs in C++. Macintosh, Unix, and Linux computers generally use a family of compilers referred to as gcc and gpp. Visual C++ builds mainly for PCs, though it can be adapted to different processors with some difficulty.


Visual Basic is extremely high-level. It is designed to be simple and fast to use; flexibility and power are sacrificed. Whereas assembly and C can be used to target any system, VB compiles only for specific builds of Windows. Bill hopes that VB will become the environment of choice for developers, making them completely dependent upon Windows products. Note that most of VB was probably programmed using C++. Thus, everything in VB can be done in C++, though the converse is definitely not true.


So, let's dive in the deep end and see if we might learn to swim.


Types and Variables


First some basics. Information theory dictates that all data can be simplified into binary strings of true and false values. Computers use this fact to represent every conceivable datum in binary. However, humans have difficulty working with ones and zeros, so computer languages provide a means of converting binary digits (bits) into human-readable types. A type is simply a way of storing information. Types are like molds or patterns for creating new data. While all types may be simplified into ones and zeros, different types are different sizes, and some may use thousands of ones and thousands of zeros to represent each specific pattern of information.


A variable is a sort of virtual container in which a datum is stored. These containers all have labels for identification, each may hold only one datum at a time, and only specific types of data may be stored in them. For example, a variable named 'age' would only hold a single number. You certainly could not assign a value of “Ryanne” to 'age', since “Ryanne” is not a number; it would not fit in the container and would not make sense anyway. Each variable has an explicit type, and only values of that type might be stored in a variable.


Actually, the term “variable” as I have been using it describes a combination of four distinct entities: an object, an address, a value, and an identifier. When a program creates a variable, volatile random-access memory (RAM) is allocated; that is, reserved for use by that variable. This block of memory, the size of which depends upon the variable's type, is called an “object”. The term “address” refers to the location in memory where the object resides. The value, of course, is the actual information, encoded in ones and zeros by the object. Notice that every object has a value and address by definition; memory must reside somewhere, and since it is made of ones and zeros, it must also have a value. The value is simply the interpretation of the binary digits, though this value is not valuable unless we organize the digits in some meaningful pattern. Luckily, C++ does this for us. Often variables also have identifiers, which are nothing more than programmer-defined labels used in code to denote a certain variable to the compiler. Identifiers only have a context in code; once the program compiles, it uses only addresses to denote variables since processors have no use for names.


I will not try to further explain the variable concept, as I am sure you grasp it already. However, realize that in C++, every variable must have a pre-defined type. We cannot create a typeless variable and expect the compiler to figure out the value of its object. Without a type—the pattern in which the data is organized—objects have no value. This is a little different than VB, since in VB dynamic variables can be defined which can change their type as the program progresses. We can do this also in C++ with a little ingenuity, and in fact we will create a dynamic variable later in this tutorial. Since C++ is lower-level than C++, we can do everything in C++ that we can do in VB, though it might be a little more complicated.


To fill a variable with a value in C++, we use the “assignment” operator (operator =). For example, if we have a variable called 'a', we could assign it the value 27:



a = 27;



Notice the semi-colon at the end of the line. In C++, every statement is terminated with a semi-colon.


After the above code executes, the variable 'a' contains the value 27, and 27 and 'a' could be used interchangeably.


To use a variable, we must first create and define one. The first step in creating a variable is called “declaration”. To declare a variable, we first list the type we want the variable to contain, and then list the name we want to associate with the variable. For example, if we had a type called 'animal', then we could declare a variable like this:


animal cage;



Now 'cage' is a variable that holds a single datum of type 'animal'. So we could literally put an animal in the cage.


We can save time (both for the processor and for the programmer) by “initializing” a variable by assigning it a value as soon as we declare it. This entire process is called a “definition”, is done simply enough:


type name = value;


In C++, as in most languages, we cannot define two variables of the same name within the same scope. We will discuss scope later, but for now realize that we can only declare each variable once.


The subtle difference between declaration and definition is negligible for now, though it becomes very important in more advanced programming. Realize for now that a variable has no meaningful value when it is declared. Of course, we could assign it a value with operator =, but until then it is meaningless. A variable that has a complete definition is usable immediately, because it is initialized with a meaningful value.


Now we will look in more depth at the fundamental types used in C++. The most basic type is a Boolean digit, named after the English mathematician George Boole, who in the 1800's invented Boolean algebra as a means of manipulating logical statements, or “propositions”. A proposition can have a value of either true or false. For example, the proposition “Ryanne is a dork” is perfectly true, while “Ryanne eats pork” is false.


As a side note, fuzzy logic is a method of representing propositions that are party true and party false. Scientists have made “fuzzy chips” that support the algebra of fuzzy statements, but we can simulate fuzzy logic in C++ without building a new computer. We will write a fuzzy program later in this tutorial.


Computers represent Boolean digits with zeros and ones. When a proposition is evaluated, the result is always a Boolean digit. For example, the proposition “one and one make two” evaluates to 1 (true) while “one equals two” evaluates to 0 (false). Boolean digits can be manipulated just like ordinary decimal numbers, but the operators programmers typically use on Boolean types are the “and,” “or,” and “equality” operators. In C++, the symbols &, |, and == are used to represent these operators respectively.


As you would expect, the equality operator (operator ==) compares its operands and returns true only if both are equal. If they are not the same, it returns false. So the proposition “1 == 1” would evaluate true while “1 == 2” would evaluate false. C++ also offers an inequality operator (operator !=), which returns true when its operands are not equal.


Be careful not to confuse the assignment operator (operator =) with the equality operator (operator ==). The similarity between these operators causes a lot of problems for beginner programmers. If you mix them up in a program, it will almost always compile and run, but will of course not work properly! The two operators are really quiet different, as you will experience later.


The “and” operator (operator &) works by comparing two Boolean values and deciding whether they are both true. If both are true, then the operator returns true. Otherwise it returns false. So, the proposition “(1 == 1) & (2 == 2)” would return 1 (true) while “(1 == 1) & (2 == 3)” would return 0 (false). Similarly, operator | returns true when either or both of the Boolean values are true. So “(1 == 1) | (2 == 3)” would evaluate to 1 (true) since at least one of the operands evaluated true.


The “or” operations actually come in two flavors: inclusive and exclusive. Inclusive-or (operator |) returns true if at least one operand is true; that is, either or both may be true. The exclusive-or (operator ^) returns true if one and only one are true. The exclusive-or operator is generally used only in bitwise operations, so it is not very common in modern programming.


In C++ the Boolean type is signified by the keyword bool. Variables of type bool can be assigned values of true or false, which are really just synonyms for 1 and 0:


bool a = true;

bool b = false;

bool c = a & b; //'c' will equal 'false'

bool d = a | b; //'d' will equal 'true'


As a side note, operator & and operator | can be doubled to speed code execution. In the assignment “a = b & c”, the program accesses all three of the variables every time. The code “a = b && c” checks 'b' first and doesn't bother accessing 'c' if 'b' is false, since the proposition will be false regardless of what 'c' holds. As a general rule, you should use && and || whenever possible instead of their slower counterparts.


The next type is that of a byte, which is 8 bits strung together in a word. In C++, bytes are signified by the char keyword. This name was chosen because any of the 255 ASCII characters can be represented with a byte, so char variables are usually used to store letters. However, they can also represent the integers from -128 to 127:


char a = 'A';

char b = 5;



Notice that the single quotes surrounding the value 'A' signify that it is of type char. Without the single quotes, the parser would think the 'A' refers to another variable.


Numbers come in many forms, so C++ offers many types for storing numbers of varying size and precision. They are int, long, float, and double. Integers can be stored as int or long; the latter allows a wider range and requires more bytes. Similarly, floating-point numbers can be float or double depending upon the required precision; the former is the smaller.


float pi = 3.14159f;

int steps = 13;



The trailing 'f' in the first line tells the parser that the constant is to be parsed as a float. Otherwise, the parser will assume it is a double, and the compiler would have to convert the double to float in order to store it in 'pi'. This really is not necessary, but is good programming practice.


Many variables can be created simultaneously and given a single name with the help of arrays. To declare an array, list the type and name as usual but include a trailing pair of square brackets ([]) as well. Place the requested size of the array within the brackets, and the compiler will reserve a block of memory equal to the size of the type multiplied by the size of the array. In other words, if we need to store 10 chars, and a char is one byte (8 bits) long, than the size of the entire array would be 10 bytes (80 bits). Notice that this means that in an array, all elements must be of the same type. We cannot store floats and chars in the same array. (Actually, this can be done, but not easily. We will make such an array later in this tutorial.)


To access the variables stored in an array, we use the “random access operator” (operator []) with the appropriate index. In C++, index 0 is the first variable; if the array has 10 elements, then the last would be at index 9. The term “random access” refers to the ability of an array to retrieve data at any point in the memory block, in any order. That is, we need not “iterate” through the array until we get to the right element; we can access any variable directly via its index:


char name[6]; //declare an array of type 'char' and size 6

name[0] = 'R';

name[1] = 'y';

name[5] = 'e'; //access the last element



The variable 'name' declared above is an array of char, so we may fill it with a list of characters. Certainly you realize the importance of a list of characters! This is one way of storing strings (groups of characters) in C++. We can initialize an array just as easily as any other variable, so long as we fill the entire array; we cannot initialize part and leave the rest empty. To initialize an array, C++ offers these conventions:


char myName[6] = {'R', 'y', 'a', 'n', 'n', 'e'};

char yourName[4] = "Mike";

int matrix[3][3] = {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}};

float line[2][2] = {{10.0f, 30.0f}, {20.0f, 40.0f}};

char girlfriend[] = "Sarah";



The first line above is a lot of typing for six letters, so C++ allows us to initialize arrays of char (strings) with double quotes ("). We use this shortcut in the second line to save some time. The next two lines illustrate "multidimensional arrays", which are basically arrays of arrays. In the last line we take advantage of another shortcut: if we initialize an array when we declare it, we need not explicitly declare the size of the array. The compiler will calculate the required size for us. So, the array 'girlfriend' as defined above will be 5 chars in size, or 5 * 8 = 40 bits long.


Be extremely careful when using arrays in C++. Arrays do not keep track of their own size; that is our responsibility as programmers. So, if we accidentally try to access the nth element of an array without n elements, the program may crash (if we are lucky). Usually, far worse things than crashes occur when we exceed the boundary of an array: the program is likely to run just fine but will behave bizarrely, because it will be accessing and using "garbage". In rare instances, we can accidentally access data in memory that is used by other programs, sometimes causing the entire system to halt. Windows does not let this happen anymore, but we must still be careful, especially when targeting obscure platforms like calculators, etc.


Arrays are very often used to store strings, as we saw above. However, C++ actually presents no standard means of storing strings. Different programmers prefer different types of strings, and oftentimes programmers must resort to using multiple methods in the same program in order to comply with different libraries. Strings are complicated in C++ and are always a problem for beginners, so we will return to discuss them later.


One last type requires discussion: the obscure, empty little type signified by the keyword void. We use this type to declare variables that hold nothing. Trust me, this feature will become important later, but we need not discuss it further for a while.


The Pointer


One of the most confusing concepts in C++ is that of the pointer. Pointers are signified by an asterisk (*). All variables reside somewhere in volatile memory, and while we rarely need to worry about their exact locations, the addresses at which they are located are just as useful as the variables themselves. We will see later why pointers are so important in C++ despite their being rarely used in other languages. But for now, just realize that pointers are simply numerical addresses that refer to the point in memory at which a variable resides. Pointers “point” to variables, but are not the variables themselves.


float a = 100.0f;

float *b = &a;



I know, this looks incredibly confusing; I am convinced that C++ was written specifically to seem complicated and hard to use, but it really is very easy. In the above code, the first line declares a variable that is stored somewhere in RAM; we do not care exactly where. The second line declares a pointer of type float that points to the variable 'a'. Here, the ampersand (&) signifies the reference operator, which returns the address of the variable it prefixes. So, say the variable 'a' is placed at address 40000. I just made that number up. Then, 'b' would just store the address 40000, which could be used later to look up the variable located there. Since the pointer is of type float, we can be sure that the datum it points to will be a float.


When we declare an array, we are actually reserving a memory block of appropriate size and storing a pointer to the beginning of that block. We can then store values at multiple points within that block by looking up the address stored in the pointer and scanning ahead to a new point within the reserved memory block. C++ does all this for us, fortunately, so that to us an array looks like just a list of variables. We will return to pointers at various points in the remainder of this tutorial, but for now their basic concept will suffice.


The Class


Arguably the most important tool available in C++ is called the class. A class is a sort of definition that is applied to any number of objects. By creating a class, we can define our own type and then create variables of that type. For example, we could create a class called 'animal' and then create a variable 'dog' of type 'animal'. So we obviously are not restricted to using only Boolean digits, numbers, and characters in our programs. We can define a class for any form of information imaginable. Later we will construct a class to represent neurons and form a neural network.


Classes have members, which are like variables within variables. We can place a class within a class, and a float within that class, and so on. To access a member of a class, we use the member operator, signified by a period (.):


dog.leg.paw.toe.nail.length = 10.0f;


To create a class, we use the class keyword, followed by the class's name in this form:


class dog

{

public:

float height;

int age;

private:

float weight;

int id;

};



This code defines a new type, so now we can create a variable of type 'dog'. The variables declared within the curly brackets in the above code become members of the class 'dog', so every variable we declare of type 'dog' will automatically contain these variables. However, all members are not created equally, and we will not be able to access some of them. The variables declared after the public: keyword will be accessible from anywhere in the program, but the variables after private: can only be accessed from within the class itself. We will see how this is useful later. Using the above class, we can code the following:


dog Ike; //declare a variable of type 'dog'

Ike.height = 2.0f;

Ike.age = 1;



I know this looks familiar. VB uses classes extensively, though I think they are called “objects” in that nomenclature. However, we are only scratching the surface of the class; we cannot understand the functionality of classes without first discussing the functions of functions.


The Function


Functions go by many names: routines, subroutines, procedures, etc., but these all refer to miniature programs located within a larger program. Functions have the ability to be called from any point in the program, which certainly makes life a lot easier. Functions typically accept data as operands or parameters, perform some transformation on this data, and then return some result back to the main program. I am sure you understand functions already, though you may not be familiar with the term, as I believe VB calls them “subroutines”.


As a general rule, functions should do one thing and one thing only. If you cannot come up with a one-word name for a function, chances are that you have made a fundamental design flaw. One way to distinguish good code from bad is by looking at the size of the functions: good programmers write functions of only a few lines each. Smaller functions increase readability and allow for much easier debugging later, especially in larger programs. Massive functions spell trouble and inexperience.


Functions are declared just like any other variable, except that they must contain a body somewhere. A function's body is defined as the code within the curly brackets immediately following its declaration. The only other difference between functions and other variables it that functions are always followed by a pair of parentheses. In fact, the only way the parser can distinguish between variables and functions is by looking for parentheses.


When declaring a function, we first list the function's return type, its name, a pair of parentheses, and then the body. Functions have types just like other variables; in fact, we can think of functions as variables that can change their values automatically. All functions must return a value, unless they are of type void. Functions of type void are sometimes called procedures because they are simply miniature programs. Most functions at least return an error code of some sort, so void functions are actually rare in advanced code, but beginners seem to use them extensively. I think they are basically useless and will rarely use them. Because functions return values, they can be used just like variables:


float foo()

{

return 1.0f;

}



If the above function is called, it will return the float 1.0f:


float a = foo(); //'a' will equal 1.0f


Functions are usually declared with their bodies immediately following. However, we often must declare a function first and define its body somewhere else later in the program. This is due to the linear nature of programs. Problems arise when, for example, two functions must call one another. If the first calls the second before the second is declared, the compiler will fail to recognize the second function's name. To combat this, C++ allows the declaration of “prototypes”, or functions without bodies. Prototypes must always be defined later with their bodies following as usual. The distinguishing trait of prototypes is that they are followed with a semi-colon (;) instead of a body. Consider the following code:


int age(); //a prototype for function 'age'

int nextYear()

{

return age() + 1;

}

int age()

{

return 16;

}



I know, this really does nothing, but it illustrates how a function can be prototyped first and declared later. With the above functions, we could do this:


int a = age(); //'a' will equal 16

int b = nextYear(); //'b' will equal 17



Functions are fairly useless unless we can feed inputs to them. We do this via “parameters”, which are variables owned by a function that are set when the function is called. To create a function with parameters, we list the variables within the parentheses after the function's name when we declare it. Parameters are separated with commas (,):


float multiply(float a, float b)

{

return a * b;

}



So this function is pointless as well, but it shows how we can declare a function with multiple parameters. Now we can do the following:


float a = multiply(2.0f, 3.0f); //'a' will equal 6.0f



Scope


By now you should have noticed that in C++ blocks are designated with pairs of curly brackets instead of the begin/end pairs of BASIC. A “block” is a section of code that executes as if it were a single statement. Blocks can be layered within themselves, creating complex hypertexts of code. Every block has its own scope, which is inherited by any blocks contained within it. That is, when a variable, type, class, or function is declared within a block, it can be accessed only within the boundaries of that block. Outside the curly brackets, the variable is “out of scope” and does not exist.


{

int a = 27;

}

int a = 3; //This is not the same 'a'! The previous one is out of scope.



When a variable leaves its scope, it is “destructed”; that is, the memory occupied by the variable is freed and the data it held is lost.


Hello World” Console App


The first program written in a foreign language is always the famous “Hello World” console program. So as not to break with tradition (programmers are unfortunately obsessed with tradition), we will write that program now:


#include <stdlib.h> //include the standard library header

#include <stdio.h> //include the standard input/output header



int main()

{

puts("Hello, World!"); //'puts' is declared in stdlib.h

return 0;

}



This takes a little explanation, but it should not look too intimidating. The first two lines are compiler directives, which are commands sent to the compiler when the source is built. Compiler directives are always prefixed with a number sign (#) and must be at the beginning of a new line. The include directive dumps other files into the source, allowing the incorporation of many files into one program. The two files we included above are the standard library and standard input/output headers. “Headers” are files that are designed so that they may be included in numerous projects, so that we do not have to rewrite the same code all the time. We need these two headers in order to get text on the screen. The 'puts(char *_s)' function is declared in the standard library, so we can use it as long as we include the appropriate header.


The 'puts' function takes a pointer to a char as a parameter, looks up the char in memory, scans forward through memory until it finds an end-of-line character, and then prints all the characters in between to the screen. This is another way of using strings in C++. Back before a standard 'string' class was created in the Standard Template Library (STL), programmers usually used char * as the sole means of storing strings. These are called “C-strings” and are dangerous and confusing to use, but since most older functions only take C-strings as their parameters, we must learn to cope with these archaic devices.


Strings in C++, remember, are denoted by double quotes. In the above code, the string “Hello, World!” is automatically converted to a C-string by the compiler, which knows that a C-string is expected by the 'puts' function. The string is only stored in memory until the 'puts' function returns, at which time the memory is released and the temporary parameter variable is destructed.


If you have been paying attention, you may have realized that an array of char and a pointer to a char are actually the same thing. Remember that when we declare an array, the compiler reserves the appropriate memory and stores a pointer to the beginning of the memory block. That pointer is what we pass to the 'puts' function. So, C-strings are really the same as arrays of char, except that we must reserve the memory block ourselves.


For this program to run, we must first set up our compiler to build it as a console application. Console apps begin execution by calling the 'main' function; that is, the 'main' function is called automatically when we run the program. Therefore, every console app must declare a 'main' function somewhere, and this function must be of type int. Notice that this means that console apps always return a value of type int, where anything but 0 represents an error.


In any program, the area outside the 'main' function's body can only contain declarations. Anything declared outside the main body becomes “global”, meaning that it can be accessed from anywhere in the program once it is declared. In other words, variables and functions declared outside the main body do not leave scope until the program terminates. All other statements and commands cannot be placed outside of the main body or the bodies of other functions called within the 'main' function.


Flow Control


All programming languages provide a means of controlling the flow of execution, and C++ is certainly no exception. The flow control commands provided in C++ are the if..else, for, while, do..while, and switch..case statements:


if (proposition)

statement

else

statement



for (declaration; proposition; statement)

statement



while (proposition)

statement



do //'statement' will execute at least once

statement

while (proposition)



switch (ordinal) //'ordinal' is a variable of type int, long, or char

{

case value:

statement

break;

case value:

statement

break; //...

default:

}


In the above pseudo-code, the blue commands are optional. I will not bother explaining these, as they have similar counterparts in VB, and are fairly self-explanatory. Looking at example source should suffice here. Remember, blocks (sections of code surrounded by curly brackets) are executed as if they were a single statement, so in any of the above control commands, the placeholder 'statement' can be replaced with a single statement or an entire block.


Object-Oriented Programming


Now that we understand the basics, we can at last move on to use the real power of C++. The most basic yet most powerful tools used in object-oriented programming are called “methods”, which are simply member functions in a class. Recall that classes are objects that contain other objects. Since functions are objects themselves, classes can contain variables, other classes, and also member functions. This looks like this:


class dog

{

public: //make the following members accessible from outside the class

float height; //a variable of type 'float'

float weight;

void walk() //a member function that takes no parameters and returns nothing

{

puts(“I'm walkin' the dog!”);

}

void sleep()

{

puts(“The dog's asleep.”);

}

};



This just defines a type named 'dog' that contains the member functions 'walk' and 'sleep'. Now, we could create a 'dog' and call either of its methods:


dog Ike; //create an object of type 'dog' named 'Ike'

Ike.walk(); //call Ike's member functions

Ike.sleep();


Binary Network Example


Scientists have studied binary networks in the past few decades because of their life-mimicking capacity to stabilize into highly ordered structures despite their random behavior. Some theorize that DNA is actually a complicated network or hypertext which corresponds to the initial state of a complicated binary network which, over the course of an organism's life, evolves into the many specialized organs required by life. In this section, we will design a class to represent binary nodes, create an algorithm for connecting these nodes into networks based upon simulated DNA, and observe the spontaneous order which arises out of this chaos.


The first step in object-oriented programming is always to construct classes to represent the objects used in the program. The vertices of a network are called nodes, and since this example involves a network, we will call the basic class a 'node'. In this network, we will spread the nodes uniformly on a 20 x 20 square grid, and we will allow nodes to be connected to the adjacent nodes positioned directly above, below, and on either side. In a binary network, as the name implies, each node can be either on or off. This condition will be called the node's 'State'. Each node follows a common set of rules, which is really arbitrary; some rules sets work and some do not, but the ones that do all seem to exhibit similar behavior. In this example we will use these rules: if a node is connected to either two or three activated (on) nodes, then it will activate; otherwise it will deactivate (off).


Now that we have a plan in mind of the way the nodes should function, we can begin programming them. Here is the implementation of the 'node' class, found in the header 'node.h':



//node.h

class node

{

public:

//defines a type 'direction' which has enumerated values of UP, DOWN, LEFT, and RIGHT

typedef enum {UP = 0, DOWN, LEFT, RIGHT} direction;



private:

node *neighbors[4]; //each is NULL if not connected



public:

bool State; //Is this node on or off?



node(); //prototype for constructor defined in node.cpp

bool update(); //prototype for methods defined in node.cpp

void connect(direction _dir, node *_node);

void reset();

};



This should make some sense. The first new thing here is the typedef at the beginning of the class. A typedef is a simple way to create a type when an entire class would be unnecessary. This typedef defines the type 'direction' as having the enumerated values listed in order within the curly brackets. The first value, 'UP', is set to 0, so the rest will be automatically enumerated as 1, 2, and 3. All this means that a variable of type 'direction' can have values of UP, DOWN, LEFT, or RIGHT, which are just meaningful representations of the numbers 0, 1, 2, and 3. It sounds complicated, but it makes things easier to read later. For example, in the prototype for the 'connect' member function near the end of the class, the first parameter is of type 'direction', which conveys a lot more information than if we had used the type int, which would have done basically the same thing.



The second section defines an array of four pointers to other nodes called 'neighbors'. Using the enumerated values we created earlier as indices, we can access the node's neighbors like this:



neighbors[UP]->State = false;



This shows a perfect example of the “indirect member access operator” (operator ->). Since 'neighbors[UP]' is a pointer and not an object, we cannot use a period to access its members (it has no members). However, since the pointer points to an object (another 'node' object) that does have members, we can indirectly access the object's members through the pointer, which is exactly what the operator -> does. So the above line simply turns off the node located above.


Another new tool demonstrated in this class is called a “constructor”. Recall that when variables go out of scope, the are automatically destructed. When they are created, they are automatically constructed. Classes are no different. When any object is created, the object's constructor is automatically called, and when it go out of scope, the destructor is called. Constructors and destructors are simply special member functions that are called automatically by the compiler. To declare a constructor, simply declare a member function of no type named the name of the class. A destructor looks the same, except it is prefixed with a tilde (~). So in the above class, the line 'node();' declares a constructor. No destructor is necessary in this class.


The reason we need a constructor in this class may not be too obvious. Since we use pointers to access the neighbor nodes, we need a constructor to initialize the pointers by setting them to 'NULL', which is usually defined as 0. That way, we can check whether the pointer actually points to something before we try to access it's members. We could check whether the pointer == NULL, which insures that it is not pointing to anything. Then we know that if we tried to indirectly access it's members, the program would crash. We also know that the node is not connected to the node in that direction.


The class above is not complete without the bodies of the member functions, which are usually kept in a separate file with a '.cpp' extension. Here is the file:




//node.cpp

#include "node.h"

#include <stdlib>



node::node() //the constructor

{

reset(); //now, the node is automatically reset when it is created

}



bool node::update()

{

int count = 0;



//count the number of nodes that are connected and activated

for (int i = 0; i < 4; ++i) //increment 'i' from 0 to 3

if (neighbors[i] != NULL) //does the pointer actually point to something?

if (neighbors[i]->State) //is the node activated?

++count; //increment the count



//each node is on only if exactly two or three connected nodes are activated

if (count == 2 || count == 3)

State = true;

else

State = false;

return State;

}



void node::connect(direction _dir, node *_node)

{

neighbors[_dir] = _node;

}



void node::reset()

{

neighbors[UP] = NULL;

neighbors[DOWN] = NULL;

neighbors[LEFT] = NULL;

neighbors[RIGHT] = NULL;

State = (bool)!(rand() % 5); //'rand()' is defined in <stdlib>

return; //return nothing.

}



Now, the class is complete and the member functions, which were just prototypes before, actually do something. The only new thing here is called the “scope resolution operator” (operator ::). We write 'node::reset()' to signify that the definition belongs to the class 'node'. Classes have their own scopes, so this just prevents the definition from being created in the global scope like a normal function. We could have defined the functions within the class definition, and then they would have been in the correct scope already, but this is rarely done. Member functions are usually declared in a separate file using the scope resolution operator.


I know I said earlier that I usually do not use void functions, but I did above for simplicity and to demonstrate that void functions do not need to return a value. In fact, it would be an error to try to return anything. If we wanted, we could include the return statement at the the end of the function, but we would leave the value after it blank. I did this in the 'node::reset()' function just to show it can be done.


The rest of the program is a lot more complicated, but basically it creates a grid of nodes, connects them together in a random pattern, and initializes them with random values. I've created a simple Windows interface to show the result graphically. Each time the 'Step' button is pressed, each node is updated using its 'update()' method. The resulting patterns are amazing. The program is here.


Hold down the Enter key to speed the process. One of four things can happen in the end. The pattern can be “open” and spread throughout the grid, consuming the entire area like a virus. Or, the pattern can be “closed” and stabilize into small, independent “cells”. These cells can be either “dead” or “alive”, depending on whether they continue to change or simply lock up and stop moving after a time.


Keep running the experiment until you see a collection of living cells. These cells are often amazingly similar to their living counterparts. For example, they create a cell wall around themselves to keep their reactions within bounds. You can manually alter the grid by unchecking the nodes with your mouse. Try pausing the process and erasing a cell's membrane. Amazingly, the cell will very often reconstruct its cell wall automatically once the process resumes.


I personally am amazed by this autopoeitic (self-creating, self-maintaining, and self-containing) behavior. In our experiment, the nodes are initialized with random states and connections. They can only be on or off, and they are restricted within a small 20 x 20 grid. Imagine, however, if the initial state of the nodes were predetermined, they could have multiple states, and they were allowed to grow unrestrictedly. Some scientists theorize that this is how DNA works. DNA might represent the initial state of a network of amino acids which react based upon a set of rules and grow into complicated cells and later organs. The evidence in this program is enough for me to believe it! Our extremely simple and limited program exhibits life-like behavior, though it is completely random.