Table of Content
- Introduction
- Manual Pages
- Visual Studios
- Header Files
- Defining Commonly Used Terms and Functions
- Topics of Interest
- Public and Private Functions and Encapsulation
Linked Topics
Here is a list of additional pages which have information of interest.
- Create Mancala in C++
- Arrays, Vectors, Header Files, Palindromes, and Structures
- Adding File Input and Output
- Adding a C++ Class and File I/O in VS Community
- C++ Using Classes to Create Banking Accounts
Introduction
C++ is an object-oriented language created at Bell Laboratories to add object-oriented programming to their C language. To properly add the object-oriented properties to the language, the design engineers modified the compiler to be much stricter in following the rules than the C language, especially about the casting of variables. For these reasons, many engineers that were still using C would use the C++ compiler to find errors that might cause problems later. Examples of some of the problems that could occur are mixing up signed and unsigned integers. Computers’ smallest storage units are bits and bytes. One bit can be considered one gate or switch, which can be either on (1) or off (0). Now, a byte is 8 bits. A signed byte can range from -127 to +127, while an unsigned byte can vary from 0 to 255. C++ is much stricter on enforcing variable integrity, so it becomes more difficult to mistakenly use an unsigned integer as a signed integer.
Visual Studio
Microsoft Visual Studio is what we use for developing C++ code. It is easy to download VS for Windows 10 (https://visualstudio.microsoft.com/vs/). You can select the C++ components required for the development of your code, do so. For Linux or MacOS, use Visual Studio Code (https://code.visualstudio.com/). After installing the basic VS Code, the C/C++ extension must be added (https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools). Do so and you will be ready to start your code development.
Manual Pages
Manual Pages are one of the best places to look for information about tools that are used in UNIX/LINUX. For tools that C++ uses that are related to the Operating System, they are also quite useful. For example, memory allocation (malloc(3)) is an important part of the language Operating System interface. The man pages are divided into related sections. Section 3 deals with memory management. Examples of man pages include:
-
- malloc(3) – Memory Allocation, must use free() to free up any allocated memory.
- free() – This is part of the malloc(3) man page (https://linux.die.net/man/3/malloc).
Header Files
All interfaces in C++ are defined through header files. These files allow the user to reference both system-defined functions and user-defined functions. There is a difference in how these two types of header files are included. System calls in C++ all start with a pound sign or hashtag (#). The command for including files starts with a #include. If the header file is a system library header file, the form is #include; while regular include files have the form #include “header.h”;. To include the system library files, one line has to follow the #include lines. It is using namespace std;
An example of the start of a C++ source file would be:
/* sample.cpp - Sample Program * Wayne Cook * 8 August 2020 * Purpose: * This program is the simplest C++ program. It shows the include files * that are needed for the main functionality to work. This section is * the introductory "block comment" that explains the purpose of the * program. Block Comments are use for large comments, while // is * put in front of single line comments. * * Include Files */ #include <input> // this is where cout is definded. #include <string> // This is for using "string" instead of char and char* using namespace std; // This says std definitions are being used.
Defining Commonly Used Terms and Functions
In C++, commonly used terms and functions can be defined to simplify your coding. For example, I like using elif
instead of typing else if all the time. I also like the Python input(string)
command. The #define statements actually put in the alternate code during the pre-compilation step, so that the compiler knows what is “really” wanted. I could use a #define statement for the input functionality also, but I prefer having actual code that only needs to be compiled once. Examples of both follow:
#define elif else if string input(string message) { string retValue; cout << message; getline(cin, retValue); return(retValue); }
Topics of Interest
The following sections include descriptions of some of the items I needed to learn to transition from C to C++. The start of the sample code here is at Common Sample.
String, Char, and Char*
String, Char, and Char* are all related, but none of them are totally the same thing. String is the more modern functionality that helps handle memory issues. Char can be assigned a single character. It is the same size as an Integer. Char* is a pointer to a list of individual characters. This is the older way of handling strings. It often requires the user to memory allocated space for the string and then free up the memory after it was used. String is a nicer construct that handles memory management for you. You can create a string and the memory needed to store the string assigned to it is automatically allocated and freed when the scope of its use is exited.
C++ Input and Output
C++ input is usually handled by the cin command. What it reads depends on the variable type into which it is putting the read value. For example:
int var; cin >> var; // an int must be typed here, or the program will error exit
will read in an integer and store it in the variable var.
For output, I use both the cout and printf() methods. I usually use cout << “Something to print.”; for most C++ output. However, if I want lines neatly formatted without having to do extra calculations, I use the printf(); statement, the f standing for formatting. This can be very helpful on tables. printf() prints a formatted string, with the parameter supplied. For example:
int i =7; printf("This will print an integer in four spaces: %4i, i\n");
You can use both output commands in the same output function. For example, the following code prints a well-formatted multiplication table.
// Print a 12 by 12 multiplication table
void printMultTable() { int i, j; // Counting variables cout << " "; // Print first upper left corner of table // Print 1 through 12 on the top row. for (i = 1; i <= 12; i++) printf("%4i", i); cout << endl; // Put in the new line. // Now create the table for (i = 1; i <= 12; i++) { printf("%4i", i); for (j = 1; j <= 12; j++) printf("%4i", i * j); cout << endl; } }
cout is a newer C++ construct. In the past C and C++ used variations of the print() functions/methods. Java still uses the print and scanner functions for output and input.
Virtual Function and Pure Virtual Function
Virtual Functions are procedures defined in a class that should be redefined by subclasses. There are two ways of writing Virtual Functions, the first is with a defined body, or default implementation and the second with no body. The pure virtual function is set equal to 0. The Virtual Function allows the user to overwrite the class definition for this function. The Pure Virtual Function forces the designer to implement the function in the subclass. Any Parent Class that includes any Pure Virtual Function cannot be instantiated on its own. Neither can any subclass be instantiated if the Pure Virtual Function is not defined.
Example of code:
Class Header Example (example.h)
{ Public: /* Standard (non-virtual) procedure that subclasses can use.*/ Const char* NonVirtName() {return “Base Defined Function”’} /* Virtual function, can be overridden, but has default */ Virtual char* VirtClassNameReturn() {return “Example”;} /* Pure Virtual, subclass must define. defined here as =0 */ Virtual char* PureVirtualReturn() = 0 }
Public and Private Functions and Encapsulation
Each class can have Public and Private Functions and Data. The Public Functions and Data are designed to be used by and visible to the Public. Private Functions and Data have some more restrictions. But definitions for both are visible in a standard header. That is why, when we designed libraries for SoftBench, we had two headers. The first header, with both the Public and Private Functions and Data, was used for internal development. The second header was what we included in the library package so that the users could interface with the appropriate Public Functions. If we had any virtual functions, we always defined a default action, so that each class can be instantiated. If the virtual function was set to “0” then the subclass must define the action that function will take before the subclass can be instantiated.
Encapsulation is the use of internal Functions and Data that are not visible to the outside user. There was some information in the classes I developed that I did not want the user to be able to either see or modify. Normally creating functions and data as Private will restrict their usage in Subclasses, but there are some ways around these restrictions in inherited classes. I do not know the exact methods at this time, but I am sure that I could remember a few, if needed.
Singleton
A singleton class is where there is only one resource available and you do not want more than one function accessing the resource at a time. The definition is through the Instance() function is defined as Static. This allows for only one class to be instantiated at a time. All communication with this item must go through this one class. There are two ways of having this available, either having the instantiation done under the main() program or having an associated flag to determine if this class is being used by another process. In either case, there needs to be an easy way for any function to determine if this resource is currently being used. There could be a wrapper class that passes on this information and does not allow the Singleton class to be instantiated more than once.
Overload, Override, and Hide
Overloading is the term with which I am familiar. Overriding and Hiding are newer (post-2005) concepts that are built on Overloading. Overloading is what you do to Virtual Functions or Operators with a default implementation. For example, the mathematical operators (+, –, *, /, <, >, =, ==, <=, etc.) and the function “print” are contextually overloaded, depending on the data types that are used with the operator or function. For example, if you add two small integers, you want to get a small integer in return. However, if you add two values that include Dollars and Cents, you want to get the proper monetary value in return. Since then, the override and hide functions have been added. In the example code I have seen, I have not seen enough differences between overloading and overriding to describe the difference. The functionality appears to be the same, but the main difference is that the override function has a reference back to the Base class virtual function definition. If you could explain the difference, I would greatly appreciate it. As for Hiding, this is defined by adding the keyword “new” at the end of the function definition. In the examples I have seen, if the function is defined as “Virtual char*VirtClassNameReturn() new” the function will perform the default function of the Base class. This again, is only what I have learned from my brief exploration today and I would love to hear a better definition.
Safely Passing a String Reference
The way I have safely passed strings is to pass the address, &address, of the string to the function and label the address as a const. If a variable is defined as a const, it can only be set once and cannot be changed afterward.
Structs (Structures) vs Classes
Structures and Classes, according to the internet, are basically defined as being the same. However, the way I have seen them used and the way I use them are slightly different. I only use structs to be a place to store needed data. I may include a struct in a class and I have a constructor and destructor in a struct to initialize the data and allocate (malloc) any needed space for items like strings, but I use a class for any manipulation of the items in the struct. I never allow a user of my library to see my structs, but have a “wrapper” class to allow the interface to the public. This probably gets back to the differences between what you consider public and what you consider private and why I thought I heard you refer to structs as private classes.
Common Sample
There is one common application that I have written in Java and Python. Here is the C++ version. Please notice the two methods for printing information are in these files, one is cout and the other is printf (or some variation thereof). Look for them. Compare and contrast the implementation in different languages. One of the digest differences is the “header” files need to be included to tell the module with the call on what needs to be specified to complete the call. For a more complete tutorial, you might read the C++ Language Tutorial.
MyBasics.h
Notice the flag at the beginning to prevent the header file to be included more than once.
#pragma once #include "stdafx.h" // This loads in the basic functions that are needed for main functionality. #include // this is where cout is definded. #include // This is for using "string" instead of char and char* using namespace std; // This is needed to say that the std definitions are being used. class MyBasics { public: MyBasics(); virtual ~MyBasics(); static int countDigits(int n); static bool isInteger(string n); static bool isPerfect(int n, bool prFactors = false); };
MyBasics.cpp
/*************** * NyBasics.cpp - 15 ctober 2016 * Author: Wayne Cook */ #include "MyBasics.h" MyBasics::MyBasics() // In C++, Constructor needs to be defined. { } MyBasics::~MyBasics() // In C++, Destructor also needs to be defined. { } int MyBasics::countDigits(int n) { //string buffer; //return(strlen(_itoa_s(n,buffer,20))); int checkNum = n, count = 0; while (checkNum > 0) { checkNum /= 10; count += 1; } return(count); } // Check if input is integer bool MyBasics::isInteger(string n) { bool returnVal = true; // Set the answer is True, unless an exception occures try { int number = atoi(n.c_str()); if (number <= 0) { returnVal = false; // Sometimes atoi reurns 0 for a non-integer input. } } catch (invalid_argument) { returnVal = false; // A non - integer input was found } return(returnVal); } bool MyBasics::isPerfect(int n, bool prFactors) { int sum = 0; if (prFactors) { cout << "Factors for " << n << ": "; // I could also use fprintf() instead of cout. } for (int factor = 1; factor <= (n / 2); factor++) { if (n%factor == 0) { if (prFactors) { cout << factor << " "; } sum += factor; } } if (prFactors) { cout <<"with sum " << sum << "\n"; } if (sum == n) { return(true); } else { return(false); } }
Febonacci.h
#pragma once class Fibonacci { public: Fibonacci(); virtual ~Fibonacci(); int* fibonacci(int n); }; #define MaxLength 512
Febonacci.cpp
#include "Fibonacci.h" Fibonacci::Fibonacci() { } Fibonacci::~Fibonacci() { } int* Fibonacci::fibonacci(int n) { int* list = new int[MaxLength]; /* Create storafe for array of 100 integers */ int index = 2; /* Keep track of what member of the array to fill. */ if (n <= 0) { list[0] = 0; /* I prefer to have only one return per function */ } else if (n < 2) { list[0] = 1; /* You may prefer return's though, your choice */ list[1] = 0; } else { int current, next; /* Needed for calculating the sequence */ current = 1; next = 2; list[0] = current; list[1] = next; while (next < n) { list[index] = current + next; current = next; next = list[index]; index++; if (next > n) { /* There is a chance that next will be larger than n */ break; /* Do not include it in the list. */ } } } list[index] = 0; /* Set endflag */ return(list); /* This is my one return for this function */ }
FebonacciSequence.cpp
This is the file that contains the main() definition. Notice that unlike Java, it does not need to be embedded in a class definition.
/** * Fibonacci Sequence - 17 October 2016 * Author: Wayne Cook * Main program that controls flow. */ #include "Fibonacci.h" #include "MyBasics.h" #include #include #include #include // this is where cout is definded. using namespace std; // But this is needed to say in is part of the std definitions int main(int argc, const char* argv[]) { Fibonacci* fibonacci = new Fibonacci(); char buffer[MaxLength]; int number; int* list; bool numCheck = false; char* enterSting; char* perfTest; // loop until a string that only has numbers is entered. enterSting = "Enter a number"; while (numCheck == false) { printf("%s: ", enterSting); fgets(buffer, MaxLength, stdin); numCheck = MyBasics::isInteger(buffer); enterSting = "There is a problem with your input, please re-enter your number"; } number = atoi(buffer); list = fibonacci->fibonacci(number); //fibonacci is an instantiation of the Fibonacci class, must use -> to access method. // Print the sequence printf("The number %d has the febonacci sequence of:\n", number); // printf statements have one quoted string and references to types of variables. for (int i = 0;i<MaxLength;i++) { if (list[i] > 0) { printf("%d ",list[i]); } else { printf("End\n"); break; } } // Determine the perfect numbers (sum of factors == number) in this range printf("The perfect numbers are:"); for (int factor = 1; factor <= number; factor++) { if (MyBasics::isPerfect(factor)) { // MyBasic has not been instatiated, must use :: to access method. printf(" %d",factor); } } printf("\n"); // Use the optional parameter to print the factors of the number if (MyBasics::isPerfect(number, true)) { perfTest = "is"; } else { perfTest = "is not"; } printf("Number %d %s a perfect number\n", number, perfTest); printf("Press Enter to exit program: "); fgets(buffer, MaxLength, stdin); fibonacci->~Fibonacci(); // Make sure class is freed up. return(0); // Return an integer to exit. }
Output Console Window
Microsoft Console windows do not allow copying of material. I hope you can read the window.
Pointers (*) and References (&)
All variables are assigned memory locations with a size based on the type of variable being defined.
Each byte of memory has a unique address. The main address needed is the pointer to the first byte of any variable. The two main symbols used are “*” and “&”. They are related, but not quite the same. Pointer (*) hold memory addresses. References (&) also hold the address of a variable but allow the variable to be accessed directly through the indirect reference. The easiest way to show these concepts is by example. The basic way of referencing a variable is just through its name. If there is no prefix to a variable, then it just contains a value.
-
- * – This is a pointer to a variable location and contains the address of that item. It does not contain the value of the item referenced.
- & – This is a reference to a variable. If in front of a passed variable to a function call, it allows that function to make all modifications to that variable to the location of that variable in the calling code. If it is used in a call to a function, it will pass the address of the variable. The called function is expected to be receiving a pointer to the variable.
The best way to show this is through examples.
Pass by Value
Pass by Value is the most commonly used way to pass a variable. The variable in the called function has no pointer or reference prefix. The calling statement call also has no prefix. The variable is passed in and the called function puts the variable on the stack as a local variable. The variable in the calling routine is not affected by the modifications mad in the called function. An example of the function and function call are:
// Pass in argument by value, should not affect the original value. void valPass(int num) { cout << "valPass: num value is " << num << endl; num += 2; cout << "valPass: new num value is " << num << endl; }
The calling line is:
valPass(passValue);
where passValue is previously defined. passValue will not be modified by the call.
Pass by Reference
This is where the & prefix is used in the called function. The addressed variable is used, so the variable in the calling routine is modified. The variable within the called function is used as an ordinary variable. The code of the function is:
// Pass by reference, to the user call there is no difference, but the value will be altered. void refPass(int &num) { cout << "refPass: num value is " << num << endl; num += 3; cout << "refPass: new num value is " << num << endl; }
The calling line looks the same, the called function grabs the address of the variable instead of just the value.
refPass(passValue);
where passValue is previously defined. passValue will be modified by the call.
Pass by Pointer
Please notice the pointer (*) in the called function and the reference (&) in the calling line. In this case you are passing the actual address of the variable and you can modify that address. Working with arrays, this could be helpful. But if you are passing a single variable, you can access memory all around it. Be careful when using these calls.
// Pass pointer to variable, call must be reference to passed value, with & in front. void ptrPass(int *num) { cout << "ptrPass: num (pointer) value is " << num << endl; cout << "ptrPass: *num value is " << *num << endl; *num += 4; cout << "ptrPass: new *num value is " << *num << endl; // Make sure you do not modify the pointer itself, unless you are working with an array. // If you do modify the pointer, you will change the memory address and perhaps access // memory that will make your program work incorrectly. num += 4; cout << "ptrPass: num (modified pointer) value is " << num << endl; cout << "ptrPass: *num value is " << *num << endl; }
and the calling line is:
ptrPass(&passValue);
where passValue is previously defined. Notice that the reference (&) prefix is used and not the pointer (*) prefix. The pointer prefix in the calling line is not understood by the compiler. The variable and the address can be modified.