1. Introduction to C++#
In this first lab we will be introducing/revisiting the fundamentals of the C++ language. C++ (pronounced “C-plus-plus”) is a general purpose high performance programming language developed by Bjarne Stroustrup in 1979. It’s an extension of the C programming language with added classes (the original name for C++ was “C with classes”) so it shares the same syntax as C. It is also similar to Java so it won’t be too difficult to make the switch from Java to C++.
In this semester we will be using OpenGL to create graphics applications. OpenGL uses C++ so its important you are comfortable with the syntax and the use of classes.
1.1. IDEs#
A C++ program is simply a set of plain text files that we instruct a compiler to convert to machine code and produce an executable. As long as you have installed the compiler and you have a text editor program you get program away to your hearts content. However, as your programs start to become increasingly sophisticated and you experience syntax errors and bugs, you will start to yearn for a better way. Fortunately there is and we can use an Integrated Development Environment (IDE).
An IDE provides a way of writing and organising your code files as well as a number of useful tools for debugging and organising your code projects. There are a number of IDEs available for working with C++ but we will look at the most common ones for Windows and macOS which are Visual Studio and Xcode respectively (I haven’t covered Linux here but I figure if you prefer to work with Linux you are able to find a suitable IDE yourself).
1.1.1. Visual Studio (Windows)#
The latest version of Visual Studio can be installed via the App Store and this has been installed on PCs in the Dalton Building.
Open Visual Studio and click on ‘Create a new project’ (or File > New > New Project if you are already in Visual Studio). Select Empty Project and click on Next.
Give your project a suitable name like
Lab01_Intro_to_cpp
, select a directory in your OneDrive area and click on Create. This will create a folder with the name of your project within which there will be a file with the extension.sln
(along with some other files). This is your project file and double clicking on this will open your project in Visual Studio.We need to add a file which will contain code to our project. Right-click on Source Files in the Solution Explorer and Add > New Item… Make sure C++ File (.cpp) is highlighted, give the file a suitable name like
main.cpp
and click Add.
1.1.2. Xcode (macOS)#
The latest version of Xcode can be installed via the Mac App Store.
Open Xcode and click on Create a new Xcode project (or if you are already in Xcode click File > New > Project or press ⇧⌘N), choose Command Line Tool and click Next.
Give your project a suitable name like
Lab01_Intro_to_cpp
and click Next, select a directory in your OneDrive area and click on Create.This will create a folder with the name of your project within which there will be a file with the extension
.xcodeproj
. This is your project file and double clicking on this will open your project in Xcode.Xcode has already given you a
main.cpp
file with a basic hello world example program.
1.2. C++ basics#
1.2.1. hello world#
We will start with the classic hello world example. In your .cpp source file, enter the following code.
// Lab01 - Introduction to C++
#include <iostream>
int main()
{
std::cout << "hello world\n" << std::endl;
return 0;
}
Compile and run the code by pressing the F5 (Visual Studio) key or ⌘R in Xcode. If you do not have any syntax errors a command window should appear with the following output.
hello world
Lets run through the code and see what each line does
Code |
Explanation |
---|---|
|
Any text between |
|
Imports the |
|
Defines a function called |
|
Calls the |
|
Sends the text string |
|
Sends the end line character to the |
|
Returns the integer |
1.2.2. Variables#
A variable is a portion of memory used to store a value. C++ uses the following types of variables.
Type |
Explanation |
---|---|
|
integers (whole numbers), e.g., 1, -2, 3 |
|
positive integers, e.g., 1, 2, 3 |
|
floating point numbers, e.g., 1.23. |
|
single character, e.g., ‘a’ (character values are surrounded by single quotes). |
|
text consisting of multiple characters, e.g., “hello world” (strings are surrounded by double quotes). |
|
Boolean values, e.g., true or false. |
A variable must be identified by a unique names which are called identifiers. An identifier can be a sequence of one or more letters, digits and underscores but must begin with a character and cannot contain spaces, punctuation marks and symbols. As long as you don’t use an identifier that conflicts with a C++ keyword (e.g., char
) you are free to choose whatever identifier you wish. It is good practice to use descriptive identifiers, e.g., position
, and where an identifier contains multiple words is it standard practice for the first letter of each additional word to be an uppercase character, e.g., framesPerSecond
.
Let’s write a simple program which converts an angle from degrees to radians (a radian is an angle measure where 1 radian is the angle subtended at the centre of a circle by an arc equal to the radius (it is the preferred unit of measurement when working with angles). Add the following code to your program.
// Degrees to radians conversion
float angleInDegrees = 45.0f;
float angleInRadians;
float pi = 3.1415927f;
angleInRadians = angleInDegrees * pi / 180.0f;
// Output result
std::cout << angleInDegrees << " degrees is equal to "
<< angleInRadians << " radians." << std::endl;
Output:
45 degrees is equal to 0.785398 radians.
When working with variables we first need to define the variable types. For example float angleInDegrees;
defined a variable with the identifier angleInDegrees
as a float. We could then store the value inputted by the user into this variable using the std::cin >>
command.
Note
When declaring float variables we should use the syntax 1.0f
instead of 1
. For example
float myVariable = 1.0f;
This is because is we were to use float myVariable = 1;
the compiler would need to generate extra code to convert from an integer value to a floating point value.
1.2.3. If statements#
The if-elseif-else statement in C++ uses the syntax
if (condition1)
{
// code to be executed if condition1 is true
}
else if (condition2)
{
// code to be executed if condition1 is false and condition2 is true
}
else
{
// code to be executed if both condition1 and condition2 are false
}
Let add the following code to our program to determine whether an angle is acute (\(<90^\circ\)) or obtuse (\(\geq 90^\circ\)).
// Determine whether it is acute or obtuse
std::cout << "\nIf statements\n-------------" << std::endl;
std::cout << angleInDegrees << " degrees is an ";
if (angleInDegrees < 90)
{
std::cout << "acute angle." << std::endl;
}
else
{
std::cout << "obtuse angle." << std::endl;
}
Output:
If statements
-------------
45 degrees is an acute angle.
1.2.4. For loop#
A for loop is used to repeat a set of commands when we know beforehand how many times we want it repeating. The C++ syntax for a for loop is as follows.
for (before statement ; condition ; after statement)
{
// block of code to be executed
}
The before statement
contains code to be executed before the for loop, the condition
is a logical check to see whether code within the for loop is executed and the after statement
is executed after the code within the for loop.
Lets add a for loop to our program to output the first 10 powers of 2.
// Output the first 10 powers of 2
std::cout << "\nFor loop\n--------" << std::endl;
int powerOfTwo = 1;
for (int i = 0; i < 10; i++)
{
powerOfTwo *= 2;
std::cout << "2^" << i + 1 << " = " << powerOfTwo << std::endl;
}
Output:
For loop
--------
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
2^6 = 64
2^7 = 128
2^8 = 256
2^9 = 512
2^10 = 1024
1.2.5. While loop#
A while loop is used to repeat a block of code as long as a specified condition(s) is reached and used when we do not know how many times a block of code is repeated (if we did a for loop would be used). The C++ syntax for a while loop is as follows.
while (condition)
{
// block of code to be executed
}
Lets add a while loop to our program calculate how many times the following expression is calculated until we get \(n = 1\) (known as the Collatz conjecture).
// Count the number of steps the Collatz sequence takes for n = 10 to reach 1
int n, step;
n = 10;
step = 0;
std::cout << "\nWhile loop\n----------\n" << n;
while (n > 1)
{
if (n % 2 == 0)
{
n /= 2;
}
else
{
n = 3 * n + 1;
}
step++;
std::cout << " -> " << n;
}
std::cout << "\n\nThe Collatz sequence took " << step << " steps to reach 1." << std::endl;
Note the x % y
operator returns the remainder when x
is divided by y
so n % 2
is 0 when n
is an even number or 1 when n
is an odd number.
Output:
While loop
----------
10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
The Collatz sequence took 6 steps to reach 1.
1.2.6. Arrays#
An array is a collection of multiple values of the same type that can be stored in a single variable. For example, we could store the co-ordinates of multiple points in an array and access all of the co-ordinates at once instead of creating separate variables for each one. The values of an array are stored in consecutive memory locations. The C++ syntax for declaring an array is to use square brackets after the array name, the values of each element in the array is then written in curly brackets. For example, define an array called indices
with the following.
// Arrays
float vertices[] =
{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
To access individual elements in an array we specify the index which is a positive integer value where the first element has the index 0, the second has the index 1 and so on. For example, add the following code to your program to print the elements of the vertices
array.
std::cout << "\nArrays\n------" << std::endl;
for (int i = 0; i < 9; i++)
{
std::cout << "vertices[" << i << "] = " << vertices[i] << std::endl;
}
Output:
Arrays
------
vertices[0] = -0.5
vertices[1] = -0.5
vertices[2] = 0
vertices[3] = 0.5
vertices[4] = -0.5
vertices[5] = 0
vertices[6] = 0
vertices[7] = 0.5
vertices[8] = 0
The size of the memory used to store an array can be determined using the sizeof
command. Add the following to your program to output the number of bytes used to story the vertices
array.
std::cout << "\nsize of vertices = " << sizeof(vertices) << std::endl;
Output:
size of vertices = 36
So 36 bytes are used to store the vertices
array. We can use the sizeof
command to return the number of bytes used to store different variable types, so a useful way to determine the number of elements in an array is to divide the memory used to store the array by the memory used for each element. For example, add the following to your program.
std::cout << "\nnumber of elements = " << sizeof(vertices) / sizeof(float) << std::endl;
Output:
number of elements = 9
1.3. Functions#
A function is a block of code that is used to perform a (usually) single action. They are useful for when we need to use similar code multiple times, instead of writing the code each time, we can define a function just once and call it to perform the action. You’ve already declared the main()
function which all C++ programs must have so lets add another function that raises a floating point number to a power. Add the following code to the top of your program (before the main()
function is declared).
float power(const float x, const int y)
{
float xPowerY = 1.0f;
for (int i = 0; i < y; i++)
{
xPowerY *= x;
}
return xPowerY;
}
Important
The C++ compiler reads source code from top to bottom so functions that are defined in the main.cpp
file need to appear above the main()
function.
Here we have declared the function called power()
that takes in inputs of a floating point variable x
and an integer variable y
and outputs a floating point variable. The values of our inputs will not change during the execution of the function so we specify them as constants using the const
keyword which makes the compiled code more efficient. We can call the function to calculate \(\text{2}^\text{10}\) by adding the following inside the main()
function.
// Call the power function
float twoPowerTen = power(2.0f, 10);
std::cout << "\nFunctions\n---------" << std::endl;
std::cout << "2^10 = " << twoPowerTen << std::endl;
Output:
Functions
---------
2^10 = 1024
1.3.1. Function parameters#
A function parameter is information that is passed into a function, for example, in our function above we passed in the parameters x
and y
. The code within a function only knows about the information passed into it via its input parameters and any global variables that are declared outside of all functions. Lets write a function that converts an angle in degrees to radians
float radians(const float angle)
{
return angle * pi / 180.0f;
}
Here our function called radians()
converts the float parameter angle
to radians by multiplying it by \(\pi / 180\) and returns it. If you attempt to compile your program you will get a compiler error that pi
is undefined. This is because pi
is a local variable for the main()
function. We can correct this using the following:
declare
pi
to be a local variable within theradians
function (preferred if we are unlikely to be usingpi
elsewhere in the program);declare
pi
to be a global variable outside of all functions (preferred ifpi
is likely to be used elsewhere in the program);delcare
pi
to be a parameter of theradians
function.
Fix your program by defining pi
to be a global variable so that it compiles. Call your function using the following code.
// Call the radians function
float angle = angleInDegrees;
angle = radians(angle);
std::cout << "\n" << angleInDegrees << " degrees is equal to "<< angle << " radians." << std::endl;
1.3.2. Pass by reference#
When we want a function to change the value of the parameters that are passed to the function we can pass a reference to the function. This is done using an ampersand &
before the name of the parameter. Change your radians
function above to the following.
void radians(float &angle)
{
angle *= pi / 180;
}
Here we have passed the reference to the angle
variable (the memory address of the variable) so the code within the function changes its value. We do not need to return anything from the function so we remove the return
command and change the output type of the function to void
. We now need to change how we call the radians()
function.
// Call the radians function
float angle = angleInDegrees;
radians(angle);
std::cout << "\n" << angleInDegrees << " degrees is equal to "<< angle << " radians." << std::endl;
1.4. Object orientated programming#
The main difference between C++ and its predecessor C is that C++ has classes which allow us to use Object Orientated Programming (OOP). Procedural programming requires use to write code that performs operations on data whereas object orientated programming allows us to create objects that include both data and code. Object orientated programming makes code easier to write and modify and is often faster than procedural programming.
1.4.1. Classes/objects#
A class defines the attributes (like variables) and methods (like functions) that the objects within the class will possess. For example, lets create Car
class which has the attributes for the make
, model
, year
and speed
of a car. The methods that apply to objects in our class may include accelerate()
, brake()
etc.
Add the following code to your program outside of any functions.
class Car {
public:
std::string make;
std::string model;
int year;
float speed = 0.0f;
};
Code |
Explanation |
---|---|
|
Keyword used to create the class |
|
An access specifier that specifies which attributes and methods can be accessed outside of the class. |
|
Defines the attribute |
We can then create an object from our Car
class and set its attributes. Lets create an object called delorean
for a 1981 DeLorean DMC-12. In your main()
function add the following code.
// Define car object
Car delorean;
delorean.make = "Delorean";
delorean.model = "DMC-12";
delorean.year = 1981;
Here to create the object we specified the class name and the name of our object, e.g., Car delorean;
. Then we define the car attributes using the name of the object followed by the name of the attribute, e.g., delorean.make
.
1.4.2. Class methods#
The methods for a class a functions that the objects in the class share. At the moment we have created a Car object but don’t have a way of seeing what the make, model or year of the object are. Lets create a method to print out the attributes. Inside the Car class definition add the following code.
// Methods
void outputDetails();
This declares a method called outputDetails()
which does not have any inputs. To define the method itself we use the syntax
<return type> <class name> :: <method name>()
{
// commands
}
So to define the outputDetails()
method add the following code outside of the Car class.
void Car::outputDetails()
{
std::cout << "\nMake: " << make
<< "\nModel: " << model
<< "\nYear: " << year << std::endl;
}
The Car::
bit tells the compiler that this method belongs to the Car
class. We could define the method inside of the class where we would not need Car::
but this is not recommended practice.
To call a method we use the syntax <object name> . <method name>(<inputs>)
. We can now print the details of the car object using the following code.
std::cout << "\nClasses\n-------" << std::endl;
delorean.outputDetails();
Output:
Make: DeLorean
Model: DMC-12
Year: 1981
Our outputDetails()
method does not require any inputs because the attributes are known to all objects of the class. Lets define a method for accelerating the car which has an input argument for the amount of acceleration. Add the following to your Car class.
void accelerate(const float);
Now define the method.
void Car::accelerate(const float increment)
{
speed = +increment;
std::cout << "\nThe car has accelerated to " << speed << " mph." << std::endl;
}
Note that we do not have to give the name of the input in the method declaration (some people choose to do so to help with the readability of the code). Now we have created the acceleration()
method lets accelerate our car to 88 mph.
// Call accelerate method
delorean.accelerate(88.0f);
Output
The car has accelerated to 88 mph.
1.4.3. Constructors#
A constructor is a special method that is automatically called when an object of a class is created. A constructor has the same name as that of the class, so for our Car
class we declare the constructor using Car();
. Now lets define the constructor so that a message is printed to the terminal.
Car::Car()
{
std::cout << "\nCar object created" << std::endl;
}
When we create our delorean
object the following is outputted
Car object created
Constructors are more useful when we use parameters to set the values of the class attributes. Modify the constructor declaration to include the parameters
Car(const std::string, const std::string, const int);
and modify the constructor definition so that the attributes are specified.
Car::Car(const std::string makeInput, const std::string modelInput, const int yearInput)
{
make = makeInput;
model = modelInput;
year = yearInput;
std::cout << "\nCar object created" << std::endl;
}
Now we can define the object attributes by creating the object using the following code.
Car delorean("DeLorean", "DMC-12", 1981);
1.4.4. Static member functions#
A static member function is a function that belongs to a class rather than an instance of the class. Static member functions are useful because we can call them without needing an object of that class. Static member functions can be declared simply by prepending the keyword static
to the function declaration.
Let’s say we want to define a function in the Car
class that converts speed from miles per hour to kilometers per hour. We declare a static member function in the class
static float mph2kph(const float);
and then define the function outside of the Car
class
float Car::mph2kph(const float speed)
{
return speed * 1.60934f;
}
We can now convert from miles per hour to kilometers per hour without needing to have a Car
object declared. If Back to the Future had been set in Europe what would the speed be required for time travel? Let’s call our static member function mph2kph()
to find out.
// Convert speed from mph to kph
std::cout << "\n" << 88 << " mph is equivalent to " << Car::mph2kph(88.0f) << " kph." << std::endl;
Which gives the output
88 mph is equivalent to 141.622 kph.
“141.622 kilometers per hour!” doesn’t quite have the same ring to it as “88 miles per hour!” does it.
1.4.5. Header files#
When dealing with larger programs and larger classes it becomes necessary to split the code over multiple files. Classes are declared in a header file which usually have the extension .hpp
(this isn’t a requirement but has become standard practice in C++ programming). The methods of a class are then defined in a separate source files which have the extension .cpp
.
Lets create a header file for our Car
class.
1.4.5.1. Visual Studio#
Right-click on the project name in the Solution Explorer and the select Add > New Item… (or press Ctrl+Shift+A).
Select C++ Class and click on Add.
Enter Car in the Class name field and change the .h file field to
Car.hpp
(we could have left this asCar.h
but I’ve always used.hpp
as the file extension for header files).This creates the header file
Car.hpp
in the Header files filter and the source fileCar.cpp
in the Source Files filter.
The header file we have created contains the following code.
#pragma once
class Car
{
};
The command #pragma once
is used to prevent multiple header files of the same name from being included in the compilation of the program.
1.4.5.2. Xcode#
Click on File > New > File… (or just press ⌘N) and select C++ File, make sure the checkbox next to Also create header file is selected and click on Next.
Enter
Car
in the Save As field and click on Next, make sure the folder where yourmain.cpp
is selected and click on Create.This creates the header file
Car.hpp
and the code fileCar.cpp
in your Xcode project.
The header file we have created contains the following code. Xcode uses include guards which perform the same function as #pragma once
.
#ifndef Car_hpp
#define Car_hpp
#include <stdio.h>
#endif /* Car_hpp */
Cut and paste the Car
class from main.cpp
into our Car.hpp
header file so that it looks like the following.
#pragma once
#include <iostream>
class Car {
public:
std::string make;
std::string model;
int year;
float speed = 0.0f;
// Constructor
Car(const std::string, const std::string, const int);
// Methods
void outputDetails();
void accelerate(const float);
static float mph2kph(const float);
};
Note that we also need the #include <iostream>
library so we can use strings and input/output commands. The methods are defined in the Car.cpp
source file. Cut and paste your Car
class methods from main.cpp
and so it looks like the following.
#include <iostream>
#include "Car.hpp"
Car::Car(const std::string makeInput, const std::string modelInput, const int yearInput)
{
make = makeInput;
model = modelInput;
year = yearInput;
std::cout << "\nCar object created" << std::endl;
}
void Car::outputDetails()
{
std::cout << "\nMake: " << make
<< "\nModel: " << model
<< "\nYear: " << year << std::endl;
}
void Car::accelerate(const float increment)
{
speed = +increment;
std::cout << "\nThe car has accelerated to " << speed << " mph." << std::endl;
}
float Car::mph2kph(const float speed)
{
return speed * 1.60934f;
}
Here we have also included the iostream
library as well as our Car.hpp
header file. We also need to make sure we include the Car.hpp
header file in main.cpp
. Compile and run the program to check everything works ok.
1.5. Exercises#
You are tasked with writing a C++ program to help the university store students’ details (name, ID number, course, marks etc.). Create a class called
Student
in a header file which has the following attributes:first name - string;
last name - string;
ID number - integer;
course - string;
level - integer;
marks - 12-element integer array (marks for four units over the three years);
Create a constructor for your class with parameters for creating an object. Use your constructor to create an object with the following attribute values.
first name: Ellie
last name: Williams
ID number: 12345678
course: Computer Science
level: 5
Create a method called
addLevelMarks()
which uses input parameters of a 4-element integer array containing unit marks and an integer variable containing the level and places the unit marks into the correct elements of themarks
array for the object. For example, if the level 5 unit marks are40, 50, 60, 70
thenmarks[4] = 40
,marks[5] = 50
etc. Use your method to updates Ellie’s marks with the following.level 4 marks: 55, 60, 72, 64;
level 5 marks: 68, 62, 74, 70.
Create a method called
outputMarks()
which outputs unit marks for each level in which a student has been enrolled, i.e.,
Ellie Williams (12345678)
Level 4: 55, 60, 72, 64
Level 5: 68, 62, 74, 70
Create a static member function called
levelAverage()
which takes in inputs of a 12-element integer array containing unit marks and an integer specifying the level and returns the average mark for that level.A student’s degree classification is determined by calculating a weighted average of the level 5 and 6 marks such that
and then checked against the table below.
weighted average |
Degree classification |
---|---|
>= 70 |
First-class |
>= 60 |
Upper second-class |
>= 50 |
Lower second-class |
>= 40 |
Third-class |
< 40 |
Fail |
Create a method called classification()
which uses your static member function from exercise 5 to calculate the weighted average for a student object and outputs the degree classification and the weighted average to the nearest integer.
Classification: xxxx (weighted average = xx).
Ellie has evaded the infected and survived another year. In level 6 they achieved marks of 72, 68, 76 and 65 (it is impressive that the university it still functioning during a world wide cordyceps pandemic). Update the object and output their level 4, 5 and 6 marks as well as their degree classification, i.e.,
Ellie Williams (12345678)
Level 4: 55, 60, 72, 64
Level 5: 68, 62, 74, 70
Level 6: 72, 68, 76, 65
Classification: xxxx (weighted average = xx).
1.6. Source code#
The source code for this lab, including the exercise solutions, can be downloaded using the links below.
Main source code file: main.cpp
Student class: Student.hpp, Student.cpp