$CPP Directive
Purpose: $CPP directive attempts C++ compliance in the translated code and also names the output file FileName.cpp. This directive is equivalent to using the BCX command line -c flag.
$CPPHDR Directive
When the $CPPHDR directive is used, the BCX translator automatically adds the following code to the .cpp file translation produced by BCX.
// Additional lines may be needed #if defined(__cplusplus) #include <iostream> #include <fstream> #include <sstream> #include <iomanip> typedef std::string stdstr; #endif
To add other Standard C++ Library header files to the BCX code to be translated use the directive
#include <Filename>
$NOIO directive
Purpose:The $NOIO directive disables the inclusion of the <iostream> C++ header file.
By default, when the $CPPHDR directive is used, the BCX translation includes the <iostream> C++ header file. You can disable the inclusion by adding the line $NOIO to your BCX program source code, placing it immediately after the $CPPHDR directive.
The C++ Tutorial for BCX Users
With the permission of the original author Eric Brasseur, the following is an abridged BCX version of Brasseur's "The C++ tutorial for C users".
Chapter 3. Console input and output streams
Input from the keyboard and output to the screen can be performed through std::cin >> and std::cout <<.
Example C03.bas:
$NOMAIN #include <iostream> FUNCTION MAIN() DIM age% DIM name$ std::cout << "This is a sample program." << std::endl std::cout << std::endl ' Just a line feed (end of line) std::cout << "Type your age : " std::cin >> age% std::cout << "Type your name: " std::cin >> name$ std::cout << std::endl std::cout << "Hello " << name$ << " you're " << age% << " years old." << std::endl std::cout << std::endl << std::endl << "Bye!" << std::endl END FUNCTION
Result:
This is a sample program. Type your age : 19 Type your name: BCX Hello BCX you're 19 years old. Bye!
Chapter 7. Global variables can be accessed even if a local variables have the same name.
A global variable can be accessed even if another variable with the same name has been declared inside the function.
Example C07.bas:
$NOMAIN #include <iostream> DIM a# = 128 FUNCTION MAIN () DIM a# = 256 std::cout << "Local a: " << a# << std::endl std::cout << "Global a: " << ::a# << std::endl END FUNCTION
Local a: 256 Global a: 128
Chapter 8. It is possible to declare a REFERENCE to another variable.
It is possible to make one variable be another.
Example C08_01.bas:
$NOMAIN #include <iostream> FUNCTION MAIN () DIM a# = 3.1415927 DIM AS DOUBLE &b = a ' b is a# b = 89 std::cout << "a# contains: " << a# << std::endl END FUNCTION
Result:
a# contains: 89
If you are used to pointers and absolutely want to know what happens, simply think
DOUBLE &b = a
is translated to
DOUBLE *b = &a
and all subsequent b are replaced by *b.
The value of REFERENCE b cannot be changed after its declaration. For example you cannot write, a few lines further,
&b = cexpecting that b is now c. It won't work. Everything is said on the declaration line of b. Reference b and variable a are married on that line and nothing will separate them.
References can be used to allow a function to modify a calling variable
Example C08_02.bas:
$NOMAIN #include <iostream> SUB Change (r AS DOUBLE &, s AS DOUBLE) r = 100 s = 200 END SUB FUNCTION MAIN () RAW AS DOUBLE k, m k = 3 m = 4 CALL Change(k, m) std::cout << k << ", " << m << std::endl END FUNCTION
Result:
100, 4
A reference can be used to let a function return a variable.
Example C08_03.bas:
$NOMAIN #include <iostream> FUNCTION biggest (r AS DOUBLE &, s AS DOUBLE &) AS DOUBLE & IF r > s THEN FUNCTION = r FUNCTION = s END FUNCTION FUNCTION MAIN () RAW AS DOUBLE k = 3 RAW AS DOUBLE m = 7 std::cout << "k: " << k << std::endl std::cout << "m: " << m << std::endl std::cout << std::endl biggest (k, m) = 10 std::cout << "k: " << k << std::endl std::cout << "m: " << m << std::endl std::cout << std::endl biggest (k, m) ++ std::cout << "k: " << k << std::endl std::cout << "m: " << m << std::endl std::cout << std::endl END FUNCTION
Result:
k: 3 m: 7 k: 3 m: 10 k: 3 m: 11
Chapter 9. Namespaces can be declared.
Namespaces can be declared. The variables declared within a namespace can be used thanks to the :: operator
Example C09.bas:
$NOMAIN #include <iostream> NAMESPACE first RAW AS INT a RAW AS INT b END NAMESPACE NAMESPACE second RAW AS DOUBLE a RAW AS DOUBLE b END NAMESPACE FUNCTION MAIN() first::a = 2 first::b = 5 second::a = 6.453 second::b = 4.1e4 std::cout << first::a + second::a << std::endl std::cout << first::b + second::b << std::endl END FUNCTION
Result:
8.453 41005
Chapter 10. A function can be declared inline
If a function contains just simple lines of code, doesn't use for loops or the like, it can be declared inline. This means its code will be inserted everywhere the function is used. That's somewhat like a macro. The main advantage is the program will be faster. A small drawback is it will be bigger, because the full code of the function was inserted everywhere it is used.
Example C10.bas:
$NOMAIN #include <iostream> inline FUNCTION hypothenuse (a AS DOUBLE, b AS DOUBLE) AS DOUBLE FUNCTION = SQRT (a * a + b * b) END FUNCTION FUNCTION MAIN () RAW AS DOUBLE k = 6, m = 9 ' Next two lines produce exactly the same result: std::cout << hypothenuse (k, m) << std::endl std::cout << SQRT (k * k + m * m) << std::endl END FUNCTION
Result:
10.8167 10.8167
Chapter 11. The exception structure has been added.
You know the classical control structures of C: *for*, *if*, *do*, *while*, *switch*... C++ adds one more control structure named EXCEPTION.
Example C11.bas:
$NOMAIN #include <iostream> FUNCTION MAIN () DIM AS INT a, b, c std::cout << "Type a number: " std::cin >> a std::cout << std::endl TRY IF a > 100 THEN THROW 100 IF a < 10 THEN THROW 10 c = a/3 THROW c CATCH (result AS INT) std::cout << "Result is: " << result << std::endl b = result + 1 END TRY std::cout << "b contains: " << b << std::endl std::cout << std::endl ' another example of EXCEPTION use: RAW zero [] = "zero" AS CHAR RAW even [] = "even" AS CHAR RAW notprime [] = "not prime" AS CHAR RAW prime [] = "prime" AS CHAR TRY IF a = 0 THEN THROW zero IF (a / 2) * 2 = a THEN THROW even XFOR INT i = 3 WHILE i <= SQRT((float)a) BY i++ std::cout << "Testing " << i << std::endl IF (a / i) * i = a THEN THROW notprime XNEXT THROW prime CATCH (conclusion AS CONST CHAR PTR) std::cout << "The number you typed is "<< conclusion << std::endl END TRY std::cout << std::endl END FUNCTION
Result:
Type a number: 5 Result is: 10 b contains: 11 The number you typed is prime
Chapter 12. A function can have default parameters.
It is possible to define default parameters for functions.
Example C12.bas:
$NOMAIN #include <iostream> FUNCTION test (a AS DOUBLE, b = 7 AS DOUBLE) AS DOUBLE FUNCTION = a - b END FUNCTION FUNCTION MAIN () std::cout << test (14, 5) << std::endl std::cout << test (14) << std::endl END FUNCTION
Result:
9 7
Chapter 13. FUNCTION OVERLOAD: several functions can be declared with the same name provided there is a difference in their parameter list.
One important advantage of C++ is the FUNCTION OVERLOAD. Different functions can have the same name provided something allows the compiler to distinguish between them: number of parameters, type of parameters...
Example C13.bas:
$NOMAIN #include <iostream> FUNCTION test (a AS DOUBLE, b AS DOUBLE) AS DOUBLE FUNCTION = a + b END FUNCTION FUNCTION test (a AS INT, b AS INT) AS INT FUNCTION = a - b END FUNCTION FUNCTION MAIN () RAW AS DOUBLE m = 7, n = 4 RAW AS INT k = 5, p = 3 std::cout << test(m, n) << " , " << test(k, p) << std::endl END FUNCTION
Result:
11 , 2
Chapter 14. The symbolic operators (+ - * / ...) can be defined for new data types
OPERATOR OVERLOADING can be used to redefine the basic symbolic operators for new kinds of parameters.
Example C14.bas:
$NOMAIN #include <iostream> TYPE bcx_vector DIM AS DOUBLE x DIM AS DOUBLE y END TYPE FUNCTION operator*(a AS DOUBLE, b AS bcx_vector) AS bcx_vector DIM AS bcx_vector r r.x = a * b.x r.y = a * b.y FUNCTION = r END FUNCTION FUNCTION MAIN () DIM AS bcx_vector k, m ' No need to type "struct bcx_vector" k.x = 2 ' To be able to write k.y = -1 ' k = bcx_vector (2, -1) m = 3.1415927 * k ' Magic! std::cout << "(" << m.x << ", " << m.y << ")" << std::endl END FUNCTION
Result:
(6.28319, -3.14159)
Chapter 15. Different functions for different data types will automatically be generated provided you define a template function.
Tired of defining the same function five times? One definition for *int* type parameters, one definition for *double* type parameters, one definition for *float* type parameters... Didn't you forget one type? What if a new data type is used? No problem: the C++ compiler can automatically generate every version of the function that is necessary! Just tell it how the function looks like by declaring a *template* function
Example C15.bas:
$NOMAIN #include <iostream> template <class ttype> FUNCTION minimum (a AS ttype, b AS ttype) AS ttype RAW AS ttype r r = a IF b < a THEN r = b FUNCTION = r END FUNCTION FUNCTION MAIN () RAW AS INT i1, i2, i3 i1 = 34 i2 = 6 i3 = minimum (i1, i2) std::cout << "Most little: " << i3 << std::endl RAW AS DOUBLE d1, d2, d3 d1 = 7.9 d2 = 32.1 d3 = minimum (d1, d2) std::cout << "Most little: " << d3 << std::endl std::cout << "Most little: " << minimum (d3, 3.5) << std::endl END FUNCTION
Result:
Most little: 6 Most little: 7.9 Most little: 3.5
Chapter 16. The keywords new and delete are much better to allocate and deallocate memory
The keywords NEW and DELETE can be used to allocate and deallocate memory. They are cleaner than the functions malloc and free from standard C.
Example C16.bas:
$NOMAIN #include <iostream> FUNCTION MAIN () RAW AS DOUBLE PTR d ' d is a variable whose purpose ' is to contain the address of a ' zone where a double is located d = NEW DOUBLE ' new allocates a zone of memory ' large enough to contain a double ' and returns its address. ' That address is stored in d. *d = 45.3 ' The number 45.3 is stored ' inside the memory zone ' whose address is given by d. std::cout << "Type a number: " std::cin >> *d *d = *d + 5 std::cout << "Result: " << *d << std::endl DELETE d ' delete deallocates the ' zone of memory whose address ' is given by pointer d. ' Now we can no more use that zone. d = NEW DOUBLE[15] ' allocates a zone for an array ' of 15 doubles. Note each 15 ' double will be constructed. ' This is pointless here but it ' is vital when using a data type ' that needs its constructor be ' used for each instance. d[0] = 4456 d[1] = d[0] + 567 std::cout << "Content of d[1]: " << d[1] << std::endl DELETE [] d ' delete [] will deallocate the ' memory zone. Note each 15 ' double will be destructed. ' This is pointless here but it ' is vital when using a data type ' that needs its destructor be ' used for each instance (the ~ ' method). Using delete without ' the [] would deallocate the ' memory zone without destructing ' each of the 15 instances. That ' would cause memory leakage. RAW AS INT n = 30 d = NEW DOUBLE[n] ' new can be used to allocate an ' array of random size. XFOR INT i = 0 WHILE i < n BY i++ d[i] = i XNEXT DELETE [] d DIM AS CHAR PTR s s = NEW CHAR[100] s$ = "Hello!" std::cout << s << std::endl DELETE [] s END FUNCTION
Result:
Type a number: 6 Result: 11 Content of d[1]: 5023 Hello!
Chapter 17. You can add METHODS to a class or struct.
In standard C a struct contains only data. In C++ a struct definition can also include functions. Those functions are owned by the struct and are meant to operate on the data of the struct. Those functions are called METHODS. The example below defines the method surface() on the struct vector.
Example C17.bas:
$NOMAIN #include <iostream> PPTYPE bcx_vector RAW AS DOUBLE x RAW AS DOUBLE y FUNCTION surface () AS DOUBLE RAW AS DOUBLE s s = x * y IF s < 0 THEN s = -s FUNCTION = s END FUNCTION END PPTYPE FUNCTION MAIN () RAW AS bcx_vector a a.x = 3 a.y = 4 std::cout << "The surface of a: " << a.surface() << std::endl END FUNCTION
Result:
The surface of a: 12
Chapter 18. The CONSTRUCTOR and the DESTRUCTOR can be used to initialize and destroy an instance of a class.
Very special and essential methods are the CONSTRUCTOR and DESTRUCTOR. They are automatically called whenever an instance of a class is created or destroyed (variable declaration, end of program, *new*, *delete*...).
The constructor will initialize the variables of the instance, do some calculations, allocate some memory for the instance, output some text... whatever is needed.
Here is an example of a class definition with two overloaded constructors
Example C18.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector () ' same name as class x = 0 y = 0 END CONSTRUCTOR CONSTRUCTOR bcx_vector (a AS DOUBLE, b AS DOUBLE) x = a y = b END CONSTRUCTOR END CLASS FUNCTION MAIN () RAW AS bcx_vector k ' bcx_vector () is called std::cout << "bcx_vector k: " << k.x << ", " << k.y << std::endl << std::endl RAW AS bcx_vector m (45, 2) ' bcx_vector (double, double) is called std::cout << "bcx_vector m: " << m.x << ", " << m.y << std::endl << std::endl k = bcx_vector (23, 2) ' bcx_vector created, copied to k, then erased std::cout << "bcx_vector k: " << k.x << ", " << k.y << std::endl << std::endl END FUNCTION
Result:
vector k: 0, 0 vector m: 45, 2 vector k: 23, 2
Chapter 19. Complex classes need the COPY CONSTRUCTOR and an overload of the = operator.
If you cast an object like a vector, everything will happen correctly. For example, if vector *k* contains *(4, 7)*, after the cast *m = k* the vector *m* will contain *(4, 7)* too. The values of k.x and k.y have simply been copied to m.x and m.y.
Now suppose you're playing with objects like the person class above. Those objects contain a pointer to a character string. If you cast the person object by writing *p = r* it is necesary that some function does the work to make *p* be a correct copy of *r*. Otherwise, p.name will point to the same physical character string as r.name. What's more, the former character string pointed to by p.name is lost and becomes a memory zombie. The result will be catastrophic: a mess of pointers and lost data. The methods that will do the job are the COPY CONSTRUCTOR and an overload of the = operator
Example C19.bas:
$NOMAIN #include <iostream> CLASS person PUBLIC: RAW AS CHAR PTR name RAW AS INT age CONSTRUCTOR person (n = "no name" AS CONST CHAR PTR, a = 0 AS INT) name = NEW CHAR[100] name$ = n$ age = a END CONSTRUCTOR CONSTRUCTOR person (s AS CONST person &) ' The COPY CONSTRUCTOR name = NEW CHAR[100] name$ = s.name$ age = s.age END CONSTRUCTOR FUNCTION operator= (s AS CONST person &) AS person& ' overload of = name$ = s.name$ age = s.age FUNCTION = *this END FUNCTION DESTRUCTOR ~person () delete [] name END DESTRUCTOR END CLASS SUB modify_person (h AS person&) h.age += 7 END SUB FUNCTION compute_person (h AS person) AS person h.age += 7 FUNCTION = h END FUNCTION FUNCTION MAIN () RAW AS person p std::cout << p.name << ", age " << p.age << std::endl << std::endl ' output: no name, age 0 RAW AS person k ("John", 56) std::cout << k.name << ", age " << k.age << std::endl << std::endl ' output: John, age 56 p = k std::cout << p.name << ", age " << p.age << std::endl << std::endl ' output: John, age 56 p = person ("Bob", 10) std::cout << p.name << ", age " << p.age << std::endl << std::endl ' output: Bob, age 10 ' Neither the copy constructor nor the overload ' of = are needed for this operation that modifies ' p since just the reference towards p is passed to ' the function modify_person: modify_person (p) std::cout << p.name << ", age " << p.age << std::endl << std::endl ' output: Bob, age 17 ' The copy constructor is called to pass a complete ' copy of p to the function compute_person. The ' function uses that copy to make its computations ' then a copy of that modified copy is made to ' return the result. Finally, the overload of = is ' called to paste that second copy inside k: = compute_person (p) std::cout << p.name << ", age " << p.age << std::endl << std::endl ' output: Bob, age 17 std::cout << k.name << ", age " << k.age << std::endl << std::endl ' output: Bob, age 24 END FUNCTION
Result:
no name, age 0 John, age 56 John, age 56 Bob, age 10 Bob, age 17 Bob, age 17 Bob, age 24
The copy constructor allows your program to make copies of instances when doing calculations. It is a key method. During calculations, instances are created to hold intermediate results. They are modified, cast and destroyed without you being aware. This is why those methods can be useful even for simple objects (see Chapter 14.).
In all the examples above, the methods are defined inside the class definition. That automatically makes them inline methods.
Chapter 20. The method bodies can be defined below the class definition.
If a method cannot be inline, or you do not want it to be inline, or if you want the class definition to contain the minimum amount of information (or you simply want the usual separate .h header file and .cpp source code file), then you need only put the prototype of the method inside the class and define the method below the class (or in a separate .cpp source file).
Example C20.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y DIM FUNCTION surface() AS DOUBLE END CLASS test1,test2 FUNCTION bcx_vector::surface() AS DOUBLE RAW AS DOUBLE s = 0 XFOR DOUBLE i = 0 WHILE i < x BY i++ s = s + y XNEXT FUNCTION = s END FUNCTION FUNCTION MAIN () RAW AS bcx_vector k k.x = 4 k.y = 5 std::cout << "Surface: " << k.surface() << std::endl END FUNCTION
Result:
Surface: 20
Chapter 21. The keyword this is a pointer to the instance a method is acting upon.
When a method is applied to an instance, that method may use the instance's variables, modify them... But sometimes it is necessary to know the address of the instance. No problem, the keyword *this* is intended for that purpose.
Example C21.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION module() AS DOUBLE FUNCTION = SQRT (x * x + y * y) END FUNCTION SUB set_length (a = 1 AS DOUBLE) RAW AS DOUBLE length length = this->module() x = x / length * a y = y / length * a END SUB END CLASS FUNCTION MAIN () RAW AS bcx_vector c (3, 5) std::cout << "The module of bcx_vector c: " << c.module() << std::endl c.set_length(2) ' Transforms c in a bcx_vector of size 2 std::cout << "The module of bcx_vector c: " << c.module() << std::endl c.set_length() ' Transforms b in an unitary bcx_vector. std::cout << "The module of bcx_vector c: " << c.module() << std::endl END FUNCTION
Result:
The module of vector c: 5.83095 The module of vector c: 2 The module of vector c: 1
Chapter 22. Arrays of instances can be declared.
Of course, it is possible to declare arrays of objects.
Example C22.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION module () AS DOUBLE FUNCTION = SQRT (x * x + y * y) END FUNCTION END CLASS FUNCTION MAIN () RAW AS bcx_vector s[1000] RAW AS bcx_vector t[3] = {bcx_vector(4, 5), bcx_vector(5, 5), bcx_vector(2, 4)} s[23] = t[2] std::cout << t[0].module() << std::endl END FUNCTION
Result:
6.40312
Chapter 23. An example of a complete class declaration
Here is an example of a full class declaration.
Example C23.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y DIM CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) DIM CONSTRUCTOR bcx_vector () DIM FUNCTION operator + (a AS bcx_vector) AS bcx_vector DIM FUNCTION operator - (a AS bcx_vector) AS bcx_vector DIM FUNCTION operator - () AS bcx_vector DIM FUNCTION operator * (a AS DOUBLE) AS bcx_vector DIM FUNCTION module() AS DOUBLE DIM SUB set_length (A AS DOUBLE = 1) END CLASS CONSTRUCTOR bcx_vector::bcx_vector (a AS DOUBLE, b AS DOUBLE) x = a y = b END CONSTRUCTOR CONSTRUCTOR bcx_vector::bcx_vector () x = 0 y = 0 END CONSTRUCTOR FUNCTION bcx_vector::operator + (a AS bcx_vector) AS bcx_vector FUNCTION = bcx_vector (x + a.x, y + a.y) END FUNCTION FUNCTION bcx_vector::operator - (a AS bcx_vector) AS bcx_vector FUNCTION = bcx_vector (x - a.x, y - a.y) END FUNCTION FUNCTION bcx_vector::operator - () AS bcx_vector FUNCTION = bcx_vector (-x, -y) END FUNCTION FUNCTION bcx_vector::operator * (a AS DOUBLE) AS bcx_vector FUNCTION = bcx_vector (x * a, y * a) END FUNCTION FUNCTION bcx_vector::module() AS DOUBLE FUNCTION = SQRT (x * x + y * y) END FUNCTION SUB bcx_vector::set_length (a AS DOUBLE) RAW AS DOUBLE length = this->module() x = x / length * a y = y / length * a END SUB FUNCTION operator << (o AS std::ostream&, a AS bcx_vector) AS std::ostream& o << "(" << a.x << ", " << a.y << ")"; FUNCTION = o END FUNCTION FUNCTION MAIN () RAW AS bcx_vector a RAW AS bcx_vector b RAW AS bcx_vector c (3, 5) a = c * 3 a = b + c c = b - c + a + (b - a) * 7 c = -c std::cout << "The module of bcx_vector c: " << c.module() << std::endl std::cout << "The content of bcx_vector a: " << a << std::endl std::cout << "The opposite of bcx_vector a: " << -a << std::endl c.set_length(2) ' Transforms c in a bcx_vector of size 2. a = bcx_vector (56, -3) b = bcx_vector (7, c.y) b.set_length() ' Transforms b in an unitary bcx_vector. std::cout << "The content of bcx_vector b: " << b << std::endl DIM AS DOUBLE k k = bcx_vector(1, 1).module() ' k will contain 1.4142. std::cout << "k contains: " << k << std::endl END FUNCTION
Result:
The module of bcx_vector c: 40.8167 The content of bcx_vector a: (3, 5) The opposite of bcx_vector a: (-3, -5) The content of bcx_vector b: (0.971275, 0.23796) k contains: 1.41421
Chapter 23a. An example of a complete class declaration with destructor.
A destructor has been added to the Chapter 23 example to show the use of the tilde "~" with destructors.
Example C23a.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y DIM CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) DIM CONSTRUCTOR bcx_vector () 'need ~ below DIM DESTRUCTOR ~bcx_vector() DIM FUNCTION operator + (a AS bcx_vector) AS bcx_vector DIM FUNCTION operator - (a AS bcx_vector) AS bcx_vector DIM FUNCTION operator - () AS bcx_vector DIM FUNCTION operator * (a AS DOUBLE) AS bcx_vector DIM FUNCTION module() AS DOUBLE DIM SUB set_length (A AS DOUBLE = 1) END CLASS CONSTRUCTOR bcx_vector::bcx_vector (a AS DOUBLE, b AS DOUBLE) x = a y = b END CONSTRUCTOR CONSTRUCTOR bcx_vector::bcx_vector () x = 0 y = 0 END CONSTRUCTOR 'need ~ below DESTRUCTOR bcx_vector::~bcx_vector() END DESTRUCTOR FUNCTION bcx_vector::operator + (a AS bcx_vector) AS bcx_vector FUNCTION = bcx_vector (x + a.x, y + a.y) END FUNCTION FUNCTION bcx_vector::operator - (a AS bcx_vector) AS bcx_vector FUNCTION = bcx_vector (x - a.x, y - a.y) END FUNCTION FUNCTION bcx_vector::operator - () AS bcx_vector FUNCTION = bcx_vector (-x, -y) END FUNCTION FUNCTION bcx_vector::operator * (a AS DOUBLE) AS bcx_vector FUNCTION = bcx_vector (x * a, y * a) END FUNCTION FUNCTION bcx_vector::module() AS DOUBLE FUNCTION = SQRT (x * x + y * y) END FUNCTION SUB bcx_vector::set_length (a AS DOUBLE) RAW AS DOUBLE length = this->module() x = x / length * a y = y / length * a END SUB FUNCTION operator << (o AS std::ostream&, a AS bcx_vector) AS std::ostream& o << "(" << a.x << ", " << a.y << ")"; FUNCTION = o END FUNCTION FUNCTION MAIN () RAW AS bcx_vector a RAW AS bcx_vector b RAW AS bcx_vector c (3, 5) a = c * 3 a = b + c c = b - c + a + (b - a) * 7 c = -c std::cout << "The module of bcx_vector c: " << c.module() << std::endl std::cout << "The content of bcx_vector a: " << a << std::endl std::cout << "The oposite of bcx_vector a: " << -a << std::endl c.set_length(2) ' Transforms c in a bcx_vector of size 2. a = bcx_vector (56, -3) b = bcx_vector (7, c.y) b.set_length() ' Transforms b in an unitary bcx_vector. std::cout << "The content of bcx_vector b: " << b << std::endl DIM AS DOUBLE k k = bcx_vector(1, 1).module() ' k will contain 1.4142. std::cout << "k contains: " << k << std::endl END FUNCTION
Result:
The module of bcx_vector c: 40.8167 The content of bcx_vector a: (3, 5) The opposite of bcx_vector a: (-3, -5) The content of bcx_vector b: (0.971275, 0.23796) k contains: 1.41421
Chapter 24. static variables inside a class definition
One or more variables in a class can be declared *static*. In which case, only one instance of those variables exist, shared by all instances of the class. It must be initialised outside the class declaration.
Example C24.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y STATIC AS INT count CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b count++ END CONSTRUCTOR DESTRUCTOR ~bcx_vector() count-- END DESTRUCTOR END CLASS RAW AS INT bcx_vector::count = 0 FUNCTION MAIN () std::cout << "Number of bcx_vectors:" << std::endl RAW AS bcx_vector a std::cout << bcx_vector::count << std::endl RAW AS bcx_vector b std::cout << bcx_vector::count << std::endl RAW AS bcx_vector PTR r, u r = NEW bcx_vector std::cout << bcx_vector::count << std::endl u = NEW bcx_vector std::cout << a.count << std::endl delete r std::cout << bcx_vector::count << std::endl delete u std::cout << b.count << std::endl END FUNCTION
Result:
Number of bcx_vectors: 1 2 3 4 3 2
Chapter 25. const variables inside a class definition.
A class variable can also be *const*ant. That's just like static, except it is given a value inside the class declaration and that value cannot be modified.
☞ A const within a CLASS must be a static const of integral or enumeration type initialized by a constant expression.
Some compilers, for example g++, have an extension that allows for const static float and doubles within a CLASS. Also, the C++0x standard has introduced a new type, constexpr, which allows in-CLASS float and doubles constants.
Example C25.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONST STATIC INT piint = 3 CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION cylinder_volume () AS DOUBLE FUNCTION = x * x / 4 * (piint + 0.14159) * y END FUNCTION END CLASS FUNCTION MAIN () std::cout << "The value of pi: " << bcx_vector::piint + 0.14159 << std::endl << std::endl bcx_vector k (3, 4) std::cout << "Result: " << k.cylinder_volume() << std::endl END FUNCTION
Result:
The value of pi: 3.14159 Result: 28.2743
Chapter 26. A class can be DERIVED from another class.
A class can be DERIVED from another class. The new class INHERITS the variables and methods of the BASE CLASS. Additional variables and/or methods can be added.
Example C26.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION module() AS DOUBLE FUNCTION = SQRT (x*x + y*y) END FUNCTION FUNCTION surface() AS DOUBLE FUNCTION = x * y END FUNCTION END CLASS CLASS trivector USING PUBLIC bcx_vector ' trivector is derived from bcx_vector PUBLIC: RAW AS DOUBLE z ' added to x and y from bcx_vector CONSTRUCTOR trivector (m=0 AS DOUBLE, n=0 AS DOUBLE, p=0 AS DOUBLE) USING bcx_vector (m, n) z = p ' bcx_vector constructor will END CONSTRUCTOR ' be called before trivector ' constructor, with parameters ' m and n. CONSTRUCTOR trivector (a AS bcx_vector) ' What to do if a bcx_vector is cast to a trivector x = a.x y = a.y z = 0 END CONSTRUCTOR FUNCTION module () AS DOUBLE ' define module() for trivector FUNCTION = SQRT (x*x + y*y + z*z) END FUNCTION FUNCTION volume () AS DOUBLE FUNCTION = this->surface() * z ' or x * y * z END FUNCTION END CLASS FUNCTION MAIN () RAW AS bcx_vector a (4, 5) RAW AS trivector b (1, 2, 3) std::cout << "a (4, 5) b (1, 2, 3) *r = b" << std::endl << std::endl std::cout << "Surface of a: " << a.surface() << std::endl std::cout << "Volume of b: " << b.volume() << std::endl std::cout << "Surface of base of b: " << b.surface() << std::endl std::cout << "Module of a: " << a.module() << std::endl std::cout << "Module of b: " << b.module() << std::endl std::cout << "Module of base of b: " << b.bcx_vector::module() << std::endl RAW AS trivector k k = a ' thanks to trivector(bcx_vector) definition ' copy of x and y, k.z = 0 RAW AS bcx_vector j j = b ' copy of x and y. b.z left out RAW AS bcx_vector PTR r r = &b std::cout << "Surface of r: " << r->surface() << std::endl std::cout << "Module of r: " << r->module() << std::endl END FUNCTION
Result:
a (4, 5) b (1, 2, 3) *r = b Surface of a: 20 Volume of b: 6 Surface of base of b: 2 Module of a: 6.40312 Module of b: 3.74166 Module of base of b: 2.23607 Surface of r: 2 Module of r: 2.23607
Chapter 27. If a method is declared virtual the program will always check the type of the instance that is pointed to and will use the appropriate method.
In the Chapter 26 example, *r->module()* calculates the vector module, using *x* and *y*, because *r* has been declared a vector pointer. The fact that *r* actually points to a trivector is not taken into account. If you want the program to check the type of the pointed object and choose the appropriate method, then you must declare that method as *virtual* inside the base class.
(If at least one of the methods of the base class is virtual then a "header" of 4 bytes is added to every instance of the classes. This allows the program to determine what a vector actually points to.) (4 bytes is probably implementation specific. On a 64 bit machine maybe it is 8 bytes...)
Example C27.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION module() AS virtual DOUBLE FUNCTION = SQRT (x*x + y*y) END FUNCTION END CLASS CLASS trivector USING PUBLIC bcx_vector PUBLIC: RAW AS DOUBLE z CONSTRUCTOR trivector (m = 0 AS DOUBLE, n = 0 AS DOUBLE, p = 0 AS DOUBLE) x = m ' Just for the game, y = n ' here I do not call the bcx_vector z = p ' constructor and I make the END CONSTRUCTOR ' trivector constructor do the ' whole job. Same result. FUNCTION module () AS DOUBLE FUNCTION = SQRT (x*x + y*y + z*z) END FUNCTION END CLASS SUB test (k AS bcx_vector &) std::cout << "Test result: " << k.module() << std::endl END SUB FUNCTION MAIN () RAW AS bcx_vector a (4, 5) RAW AS trivector b (1, 2, 3) std::cout << "a (4, 5) b (1, 2, 3)" << std::endl << std::endl RAW AS bcx_vector PTR r r = &a std::cout << "module of bcx_vector a: " << r->module() << std::endl r = &b std::cout << "module of trivector b: " << r->module() << std::endl test (a) test (b) RAW AS bcx_vector &s = b std::cout << "module of trivector b: " << s.module() << std::endl END FUNCTION
Result:
a (4, 5) b (1, 2, 3) module of bcx_vector a: 6.40312 module of trivector b: 3.74166 Test result: 6.40312 Test result: 3.74166 module of trivector b: 3.74166
Chapter 28. A class can be derived from more than one base class.
Maybe you wonder if a class can be derived from more than one base class. The answer is yes.
Example C28.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION surface() AS DOUBLE FUNCTION = fabs (x * y) END FUNCTION END CLASS CLASS number PUBLIC: RAW AS DOUBLE z CONSTRUCTOR number (a AS DOUBLE) z = a END CONSTRUCTOR FUNCTION is_negative () AS INT IF z < 0 THEN FUNCTION = 1 ELSE FUNCTION = 0 END IF END FUNCTION END CLASS CLASS trivector USING PUBLIC bcx_vector, PUBLIC number PUBLIC: CONSTRUCTOR trivector(a=0 AS DOUBLE, b=0 AS DOUBLE, c=0 AS DOUBLE) USING bcx_vector(a,b), number(c) END CONSTRUCTOR ' The trivector constructor calls the bcx_vector ' constructor, then the number constructor, ' and in this example does nothing more. FUNCTION volume() AS DOUBLE FUNCTION = fabs (x * y * z) END FUNCTION END CLASS FUNCTION MAIN () RAW AS trivector a(2, 3, -4) std::cout << a.volume() << std::endl std::cout << a.surface() << std::endl std::cout << a.is_negative() << std::endl END FUNCTION
Result:
24 6 1
Chapter 29. Class derivation allows you to write generic methods.
Class derivation allows you to construct more complex classes built from base classes. There is another application of class derivation: allowing the programmer to write generic functions.
Suppose you define a base class with no variables. It makes no sense to use instances of that class inside your program. But then you write a function whose purpose it is to sort instances of that class. That function will be able to sort any type of object provided it belongs to a class derived from that base class! The only condition is that inside of each derived class definition, all methods that the sort function needs are correctly defined.
Example C29.bas:
$NOMAIN #include <iostream> CLASS octopus PUBLIC: IMPFUNCTION module() = 0 AS virtual DOUBLE ' = 0 implies function is not ' defined. This makes instances ' of this CLASS cannot be declared. END CLASS FUNCTION biggest_module (a AS octopus &, b AS octopus &, c AS octopus &) AS DOUBLE RAW r = a.module() AS DOUBLE IF b.module() > r THEN r = b.module() IF c.module() > r THEN r = c.module() FUNCTION = r END FUNCTION CLASS bcx_vector USING PUBLIC octopus PUBLIC: RAW AS DOUBLE x RAW AS DOUBLE y CONSTRUCTOR bcx_vector (a = 0 AS DOUBLE, b = 0 AS DOUBLE) x = a y = b END CONSTRUCTOR FUNCTION module() AS DOUBLE FUNCTION = SQRT (x * x + y * y) END FUNCTION END CLASS CLASS number USING PUBLIC octopus PUBLIC: RAW AS DOUBLE n CONSTRUCTOR number (a = 0 AS DOUBLE) n = a END CONSTRUCTOR FUNCTION module() AS DOUBLE IF n >= 0 THEN FUNCTION = n FUNCTION = -n END FUNCTION END CLASS FUNCTION MAIN () RAW AS bcx_vector k (1,2), m (6,7), n (100, 0) RAW AS number p (5), q (-3), r (-150) std::cout << biggest_module (k, m, n) << std::endl std::cout << biggest_module (p, q, r) << std::endl std::cout << biggest_module (p, q, n) << std::endl END FUNCTION
Result:
100 150 100
Chapter 30. ENCAPSULATION: public, protected and private
The *PUBLIC:* directive means the variables or the methods below can be accessed and used everywhere in the program.
If you want the variables and methods to be accessible only to methods of the class AND to methods of derived classes, then you must put the keyword *protected:* before them.
If you want variables or methods to be accessible ONLY to methods of the class, then you must put the keyword *private:* before them.
The fact that variables or methods are declared private or protected means that nothing external to the class can access or use them. That's ENCAPSULATION. (If you want to give a specific function the right to access those variables and methods, then you must include that function's prototype inside the class definition, preceded by the keyword *friend*.)
Good practice is to encapsulate all the variables of a class. This can sound strange if you're used to structs in C. Indeed a struct only makes sense if you can access its data... In C++ you have to create methods to access the data inside a class. The example below uses the basic example of chapter 17, yet declares the class data to be protected.
Example C30_01.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PROTECTED: RAW AS DOUBLE x RAW AS DOUBLE y PUBLIC: SUB set_x (n AS INT) x = n END SUB SUB set_y (n AS INT) y = n END SUB FUNCTION surface () AS DOUBLE RAW AS DOUBLE s s = x * y IF s < 0 THEN s = -s FUNCTION = s END FUNCTION END CLASS FUNCTION MAIN () RAW AS bcx_vector a a.set_x (3) a.set_y (4) std::cout << "The surface of a: " << a.surface() << std::endl END FUNCTION
Result:
The surface of a: 12
The example above is a bit odd since the class data x and y can be set but they cannot be read back. Any attempt in function main () to read a.x or a.y will result in a compilation error. In the next example, x and y can be read back.
Example C30_02.bas:
$NOMAIN #include <iostream> CLASS bcx_vector PROTECTED: RAW AS DOUBLE x RAW AS DOUBLE y PUBLIC: SUB set_x (n AS INT) x = n END SUB SUB set_y (n AS INT) y = n END SUB FUNCTION get_x () AS DOUBLE FUNCTION = x END FUNCTION FUNCTION get_y () AS DOUBLE FUNCTION = y END FUNCTION FUNCTION surface () AS DOUBLE RAW AS DOUBLE s s = x * y IF s < 0 THEN s = -s FUNCTION = s END FUNCTION END CLASS FUNCTION MAIN () RAW AS bcx_vector a a.set_x (3) a.set_y (4) std::cout << "The surface of a: " << a.surface() << std::endl std::cout << "The width of a: " << a.get_x() << std::endl std::cout << "The height of a: " << a.get_y() << std::endl END FUNCTION
Result:
The surface of a: 12 The width of a: 3 The height of a: 4
Chapter 31. Brief examples of file I/O
Let's talk about input/output. In C++ that's a very broad subject. Here is a program that writes to a file
Example C31_01.bas:
$NOMAIN #include <iostream> #include <fstream> FUNCTION MAIN () RAW AS std::fstream f f.open("test.txt", std::ios::out) f << "This is a text output to a file." << std::endl RAW AS DOUBLE a = 345 f << "A number: " << a << std::endl f.close() std::cout << "test.txt created" << std::endl END FUNCTION
Result:
test.txt created
The content of file test.txt should be
This is a text output to a file. A number: 345
Before you use Example C31_02 be sure to create the test.txt file with Example C31_01.
Example C31_02.bas:
$NOMAIN #include <iostream> #include <fstream> FUNCTION MAIN () RAW AS std::fstream f RAW AS CHAR c std::cout << "What's inside the test.txt file" << std::endl std::cout << std::endl f.open("test.txt", std::ios::in) WHILE NOT f.eof() f.get(c) 'Or c = f.get() std::cout << c WEND f.close() END FUNCTION
Result:
What's inside the test.txt file This is a text output to a file. A number: 345
Chapter 32. Character arrays can be used like files
Generally speaking, it is possible to do on character arrays the same operations as on files. This is very useful to convert data or manage memory arrays.
Here is a program that writes inside a character array.
Example C32_01.bas:
$NOMAIN #include <iostream> #include <strstream> FUNCTION MAIN () RAW AS CHAR a[1024] RAW AS std::ostrstream b(a, 1024) b.seekp(0) ' Start from first char. b << "2 + 2 = " << 2 + 2 << std::ends ' ( ends, not std::endl ) ' ends is simply the ' null character '\0' std::cout << a << std::endl RAW AS DOUBLE v = 2 a$ = "A sinus: " b.seekp(LEN (a)) b << "sin (" << v << ") = " << SIN(v) << std::ends std::cout << a << std::endl END FUNCTION
Result:
2 + 2 = 4 A sinus: sin (2) = 0.909297
A program that reads from a character string
Example C32_02.bas:
$NOMAIN #include <iostream> #include <strstream> FUNCTION MAIN () RAW AS CHAR a[1024] RAW AS std::istrstream b(a, 1024) a$ = "45.656" RAW AS DOUBLE k, p b.seekg(0) ' Start from first character. b >> k k = k + 1 std::cout << k << std::endl a$ = "444.23 56.89" b.seekg(0) b >> k >> p std::cout << k << ", " << p + 1 << std::endl END FUNCTION
Result:
46.656 444.23, 57.89
Chapter 33. An example of formatted output
This program performs formatted output two different ways. Please note the *width()* and *setw()* MODIFIERS are only effective on the next item output to the stream. Subsequent items will not be influenced.
Example C33.bas:
$NOMAIN #include <iostream> #include <iomanip> FUNCTION MAIN () RAW AS INT i std::cout << "A list of numbers:" << std::endl XFOR i = 1 WHILE i <= 1024 BY i *= 2 std::cout.width (7) std::cout << i << std::endl XNEXT std::cout << "A table of numbers:" << std::endl XFOR i = 0 WHILE i <= 4 BY i++ std::cout << std::setw(3) << i << std::setw(5) << i * i * i << std::endl XNEXT END FUNCTION
Result:
A list of numbers: 1 2 4 8 16 32 64 128 256 512 1024 A table of numbers: 0 0 1 1 2 8 3 27 4 64