Let's break it down:
// helloworld.cpp
#include <iostream>
int main() {
std::cout << "Hello, world!\n";
return 0;
}
For CSE machines:
g++ -std=c++17 -o helloworld helloworld.cpp
clang++ -std=c++17 -o helloworld helloworld.cpp
For other, see Webcms3
// helloworld.cpp
#include <iostream>
int main() {
std::cout << "Hello, world!\n";
return 0;
}
When you stream something to STDOUT it gives stored in a buffer
This buffer is eventually "flushed" (e.g. sent to terminal)
We will use "\n" in this course as it allows more control over when the buffer is flushed, which is important on devices with limitations on performance capabilities.
// The following two are equivalent
std::cout << "\n" << std::flush;
std::cout << std::endl;
There are other basic types, but you will not need them for this course, and will rarely need them in industry.
Type | What it stores |
---|---|
bool | True or false |
int | Whole numbers |
double | Real numbers |
char | A single character |
string | Text |
enum | A single option from a finite, constant set |
T* | Raw pointers. Avoid using until we explain when, and when not, to use them |
We will cover this much later in the course.
In the meantime just know that implicit type conversion may happen and not cause any runtime errors
bool b1 = 10; // b1 becomes true
bool b2 = 0.0; // b2 becomes false
int i1 = true; // i1 becomes 1
int i2 = false; // i2 becomes 0
Basic C++ operators are very similar to your basic C operators, E.G.
During the course we will talk about different types of errors:
int main() {
// No type specified.
a = 5;
}
#include <string>
int main() {
std::string s = "";
s.at(0);
}
int main() {
int a = 3;
int b = 4;
int c = 5;
// Order of operations.
int average = a + b + c / 3;
}
Compile time
Runtime (Exception)
Runtime (Logic)
#include <string>
int main() {
std::string s = "";
s[0];
}
(Runtime) Undefined
behaviour
// linker1.cpp
#include <iostream>
int Foo();
int main() {
std::cout << Foo();
}
Link time
Type of literals | Examples |
---|---|
Boolean | true, false |
Character | 'a', '\n' |
Integer | 20, 0x14, 20L |
Floating-point | 12.3, 1.23e4, |
String (these are not std::strings) | "Healthy Harold", "a" |
void DeclaredFn(int arg);
class DeclaredClass;
// This class is defined, but not all the methods are.
class A {
int DeclaredMethod(double);
int DefinedMethod(int arg) { return arg; }
}
// These are all defined.
int DefinedFn() { return 1; }
int i;
int j = 1;
std::vector<double> vd;
#include <iostream>
#include <vector>
int main() {
const int i = 0; // i is an int
i++; // not allowed
std::cout << i << '\n'; // allowed
const std::vector<int> vec;
vec[0]; // allowed
vec[0]++; // not allowed
vec.push_back(0); // not allowed
}
int i = 1;
int j = 2;
int& k = i;
k = j; // This does not make k reference j instead of i. It just changes the value.
std::cout << "i = " << i << ", j = " << j << ", k = " << k << '\n';
int i = 1;
const int& ref = i;
std::cout << ref << '\n';
i++; // This is fine
std::cout << ref << '\n';
ref++; // This is not
const int j = 1;
const int& jref = j; // this is allowed
int& ref = j; // not allowed
Type | What it stores | Common usages |
---|---|---|
std::optional<T> | 0 or 1 T's | A function that may fail |
std::vector<T> | Any number of T's | Standard "list" type |
std::unordered_map<KeyT, ValueT> | Many Key / Value pairs | Standard "hash table" / "map" / "dictionary" type |
#include <unordered_map>
#include <vector>
// The following items are all function DECLARATIONS
// Not allowed - type templates are not types
std::vector GetVector();
// std::vector<int> and std::vector<double> are valid types.
std::vector<int> GetIntVector();
std::vector<double> GetDoubleVector();
// So is combining types
std::vector<std::unordered_map<int, std::string>> GetVectorOfMaps();
auto i = 0; // i is an int
std::vector<int> fn();
auto j = fn(); // j is std::vector<int>
// Pointers
int i;
const int *const p = i;
auto q = p; // const int*
auto const q = p;
// References
const int &i = 1; // const int&
auto j = i; // int
const auto k = i; // const int
auto &r = i; // int&
string Rgb(short r = 0, short g = 0, short b = 0);
Rgb();// rgb(0, 0, 0);
Rgb(100);// Rgb(100, 0, 0);
Rgb(100, 200); // Rgb(100, 200, 0)
Rgb(100, , 200); // error
foo(int bar); // not ok
int foo(int bar); // OK
void bar(); // OK
#include <iostream>
void swap(int x, int y) {
int tmp;
tmp = x;
x = y;
y = tmp;
}
int main() {
int i = 1, j = 2;
std::cout << i << " " << j << std::endl;
swap(i, j);
std::cout << i << " " << j << std::endl;
}
#include <iostream>
void swap(int *x, int *y) {
int tmp = *x;
*x = *y;
*y = tmp;
}
int main() {
int i = 1, j = 2;
std::cout << i << " " << j << std::endl;
swap(&i, &j);
std::cout << i << " " << j << std::endl;
}
#include <iostream>
void swap(int& x, int& y) {
int tmp;
tmp = x;
x = y;
y = tmp;
}
int main() {
int i = 1, j = 2;
std::cout << i << " " << j << std::endl;
swap(i, j);
std::cout << i << " " << j << std::endl;
}
void swap(int i, int j); // 1st-year style
void swap(int& i, int& j); // C++ style
// Note that C does not support
// pass-by-reference. This is pass-by-value.
// C courses often call this
// pass-by-reference because this is
// the closest C has to it.
void swap(int* i, int* j); // C style
int i = 5;
i = i + 1;
5
i 0x200
void Print(double d); // (1)
int Print(std::string s); // (2)
void Print(char c); // (3)
Print(3.14); // call (1)
Print("hello World!"); // call (2)
Print('A'); // call (3)
Errors in function matching are found during compile time
Return types are ignored. Read more about this here.
void G();
void F(int);
void F(int, int);
void F(double, double = 3.14);
F(5.6); // calls f(double, double)
When doing call by value, top-level const has no effect on the objects passed ot the function. A parameter that has a top-level const is indistinguishable from the one without
// Top-level const ignored
Record Lookup(Phone p);
Record Lookup(const Phone p); // redefinition
// Low-level const not ignored
Record Lookup(Phone &p); (1)
Record Lookup(const Phone &p); (2)
Phone p;
const Phone q;
Lookup(p); // (1)
Lookup(q); // (2)
// Beats a #define any day.
constexpr int max_n = 10;
// This can be called at compile time, or at runtime
constexpr int ConstexprFactorial(int n) {
return n <= 1 ? 1 : n * ConstexprFactorial(n - 1);
}
constexpr int tenfactorial = ConstexprFactorial(10);
// This may not be called at compile time
int Factorial(int n) {
return n <= 1 ? 1 : n * Factorial(n - 1);
}
// This will fail to compile
constexpr int ninefactorial = Factorial(9);
// path/to/hello_world.h
#ifndef HELLO_WORLD_H
#define HELLO_WORLD_H
void HelloWorld();
#endif // HELLO_WORLD_H
// path/to/hello_world.cpp
#include "path/to/hello_world.h"
#include <iostream>
void HelloWorld() {
std::cout << "Hello world\n";
}
// main.cpp
#include "path/to/hello_world.h"
int main() {
helloWorld();
}
g++ -c printer.cpp -Wall -Werror -std=c++17 -<more args>
g++ -c hello_world.cpp -Wall -Werror -std=c++17 -<more args>
g++ main.cpp hello_world.o printer.o -Wall -Werror -std=c++17 -<more args>
// path/to/hello_world.h
#ifndef HELLO_WORLD_H
#define HELLO_WORLD_H
void HelloWorld();
#endif // HELLO_WORLD_H
// path/to/hello_world.cpp
#include "path/to/hello_world.h"
#include "path/to/printer.h"
void HelloWorld() {
print("Hello world");
}
// path/to/binary/main.cpp
#include "path/to/hello_world.h"
int main() {
helloWorld();
}
// path/to/printer.h
#ifndef PRINTER_H
#define PRINTER_H
#include <string>
void Print(std::string);
#endif // PRINTER_H
// path/to/printer.cpp
#include "path/to/printer.h"
#include <iostream>
#include <string>
void Print(std::string s) {
std::cout << s << '\n';
}
// path/to/BUILD
cc_library(
name = "hello_world",
srcs = ["hello_world.cpp"],
hdrs = ["hello_world.h"],
deps = []
)
cc_library(
name = "printer",
srcs = ["printer.cpp"]
hdrs = ["printer.h"],
deps = [
# If it's declared within the same build
# file, we can skip the directory
":hello_world"
]
)
// path/to/binary/BUILD
cc_binary(
name = "main"
srcs = ["main.cpp"],
deps = [
"//path/to:hello_world"
]
)