Hello World
Fail: hello.cpp
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
return 0;
}
std::cout
is the “character output stream”. It is pronounced “see-out”-
<<
is an operator that comes right after it
#include <iostream>
This is known as a pre-processor directive. It instructs the compiler to locate the file that contains code for a library known as iostream
. This library contains code that allows for input and output, such as displaying data in the terminal window, or reading input from your keyboard.
int main() {
// Statements
}
Every C++ program must have a function called main()
. A function is basically a sequence of instructions for the computer to execute. This main()
function houses all of our instructions for our program.
Output a variable score:
std::cout << score << "\n";
Compile and execute
C++ is a compiled language. That means that to get a program to run, you must first translate it from the human-readable form to something a machine can “understand.” That translation is done by a program called a compiler.
What you read and write is called source code, and what the computer executes is called executable, object code, or machine code (a machine language
Typically C++ source code files are given the suffix: .cpp (ex: hello.cpp) or .h (ex: std_lib_facilities.h).
Course code (hell.cpp) > Prepocessor > Compiler > Linker > Executable (a.out).
A compiler translates the C++ program into machine language code which it stores on the disk as a file with the extension .o (e.g. hello.o). A linker then links the object code with standard library routines that the program may use and creates an executable image which is also saved on disk, usually as a file with the file name without any extension (e.g. hello).
The executable is loaded from the disk to memory and the computer’s CPU (Central Processing Unit) executes the program one instruction at a time.
Compile using default executable file name:
$ g++ hello.cpp
Execute on bash (terminal):
$ ./a.out
Renaming the executable file
$ g++ hello.cpp -o hello
$ ./hello
For review, #include
is a preprocessor directive that tells the compiler to include whatever library that follows.
Programs with multiple .cpp files need to be linked at compile time:
g++ main.cpp fns.cpp
Comments
Single line comments:
// Prints "hi!" to the terminal
std::cout << "hi!"; // print
Multi-line comment:
/* This is all commented.
std::cout << "hi!";
None of this is going to run! */
return 0;
The return
statement is used to end a function. If the program reaches this statement, returning a value of 0
is an indication to the operating system that the code executed successfully. This line of code is optional.
return 0;
Variables
A variable is simply a name that represents a particular piece of your computer’s memory that has been set aside for you to store, retrieve, and use data.
Every variable has a data type.
Basic data types
- int – integer numbers. Example: 420
- double – floating point numbers. Example: 3.14
- char – individual characters. Example: ‘a’
- string – sequence of characters. Example: “Hello World”
- bool – truth values. Example: false
Declareing a Variable
A variable represents a particular piece of computer’s memory that has been set aside for you to use to store, retrieve, and manipulate data.
Every variable in C++ must be declared before it can be used!
int number;
Initialize a variable:
year = 2019;
Declaring and initializing variable. It is highly suggested to initialize a variable before using it later.
int score = 0;
Variable names consist only of upper/lower case letters, digits, and/or underscores.
C++ is known as a strongly typed language. If you try to give an integer value a decimal number, you are going to get unexpected results, warnings, or errors.
The Scope of Things
- Variables defined in global scope are accessible throughout the program
- Variables defined in a function have local scope and are only accessible inside the function
Arithmetic Operators
+
addition-
subtraction*
multiplication/
division-
%
modulo (divides and gives the remainder)
Single equal sign =
indicates assignment, not equality in the mathematical sense.
Chaining
We use quotes when we want a literal string. We don’t use quotes when we refer to the value of variable.
int age = 28;
std::cout << "Hello, I am ";
std::cout << age;
std::cout << " years old\n";
We can use multiple << operators to chain the things we want to output. This is called chaining:
int age = 28;
std::cout << "Hello, I am " << age << " years old\n";
User Input
std::cout << "Enter your password: ";
std::cin >> password;
The name cin
refers to the standard input stream (pronounced “see-in”, for character input). The second operand of the >>
operator (“get from”) specifies where that input goes.
Relational Operators
==
equal to!=
not equal to>
greater than<
less than>=
greater than or equal to<=
less than or equal to
Logical Operators
Logical operators are used to combine two or more conditions. They allow programs to make more flexible decisions. The result of the operation of a logical operator is a bool
value of either true
or false
.
&&
: theand
logical operator||
: theor
logical operator!
: thenot
logical operator
And: &&
The and
logical operator is denoted by &&
. It returns true
if the condition on the left and the condition on the right are both true
. Otherwise, it returns false
.
( 1 < 2 && 2 < 3 )
returnstrue
( 1 < 2 && 2 > 3 )
returnsfalse
The keyword and
can also be used in the place of &&
.
#include <iostream>
int main() {
int hunger = true;
int anger = true;
if(hunger and anger){
std::cout << "Hangry\n";
}
}
Or: II
The or
logical operator ||
returns true
when the condition on the left is true
or the condition on the right is true
. Only one of them needs to be true
.
( 1 < 2 || 2 > 3 )
returnstrue
( 1 > 2 || 2 > 3 )
returnsfalse
The keyword or
can be used in the place of ||
.
#include <iostream>
int main() {
int day = 6;
if (day == 6 || day == 7){
std::cout << "Weekend\n";
}
}
Not: !
The not
logical operator !
reverses the bool
outcome of the expression that immediately follows.
( !true )
returnsfalse
( !false )
returnstrue
( !(10 < 11) )
returnsfalse
The keyword not
can be used in the place of !
.
#include <iostream>
int main() {
bool logged_in = false;
if (!logged_in){
std::cout << "Try again\n";
}
}
If, Else and Else If
An if
statement is used to test an expression for truth and execute some code based on it. If the condition is true
, then the statements within are executed. Otherwise, the statements are skipped and the program continues on.
if (flip == 1) {
std::cout << "Heads\n";
}
- Inside the parentheses
()
, a condition is provided that evaluates totrue
orfalse
- If the condition evaluates to
true
, the code inside the curly braces{}
runs, or executes
We can also add an else
clause to an if
statement to provide code that will only be executed if the condition is false
.
if (coin == 1) {
std::cout << "Heads\n";
}
else {
std::cout << "Tails\n";
}
The else if
statement always comes after the if
statement and before the else
statement. The else if
statement also takes a condition.
if (grade == 9) {
std::cout << "Freshman\n";
}
else if (grade == 10) {
std::cout << "Sophomore\n";
}
else if (grade == 11) {
std::cout << "Junior\n";
}
else if (grade == 12) {
std::cout << "Senior\n";
}
else {
std::cout << "Super Senior\n";
}
Switch Statement
A switch
statement provides an alternative syntax that is easier to read and write.
switch (grade) {
case 9:
std::cout << "Freshman\n";
break;
case 10:
std::cout << "Sophomore\n";
break;
case 11:
std::cout << "Junior\n";
break;
case 12:
std::cout << "Senior\n";
break;
default:
std::cout << "Invalid\n";
break;
}
- The
switch
keyword initiates the statement and is followed by()
, which contains the value that each case will compare. In the example, the value or expression of the switch statement isgrade
. One restriction on this expression is that it must evaluate to an integral type (int
,char
,short
,long
,long long
, orenum
). - The
case
keyword checks if the expression matches the specified value that comes after it. The value following the first case is9
. If the value ofgrade
is equal to9
, then the code that follows the:
would run. - The
break
keyword tells the computer to exit the block and not execute any more code or check any other cases inside the code block. Without thebreak
keyword at the end of each case, the program would execute the code for all matching cases and thedefault
code as well. - At the end of each switch statement, there is a
default
statement. If none of the cases aretrue
, then the code in thedefault
statement will run.
Loops
A loop is a programming tool that repeats some code or a set of instructions until a specified condition is reached. When we see that a process has to repeat multiple times in a row, we write a loop. Loops allow us to create efficient code that automates processes to make scalable, manageable programs.
While Loop
While
loop will continue to execute the code inside of it, over and over again, as long as the condition is true
.
int guess = 0;
std::cin >> guess;
while (guess != 8) {
std::cout << "Wrong guess, try again: ";
std::cin >> guess;
}
&&
symbol means and and it combines two conditions into one
For Loop
When we know exactly how many times we want to iterate (or when we are counting), we can use a for
loop instead of a while
loop:
for (int i = 0; i < 20; i++)
{
std::cout << "I will not throw paper airplanes in class.\n";
}
- The initialization of a counter:
int i = 0
- The continue condition:
i < 20
- The change in the counter (in this case an increment):
i++
Vectors
A vector is a sequence of elements that you can access by index. The first index in a vector is 0
.
The std::vector
lives in the <vector>
header.
#include <vector>
And the syntax to create a vector looks like:
std::vector<type> name;
// Example int vector:
std::vector<int> calories_today;
// string vector:
std::vector<std::string> last_jedi;
Inside the angle brackets is the data type of the vector. After the angle brackets is the name of the vector. The type of the vector cannot be changed after the declaration.
We can also initialize a vector, giving it values, as we are creating it in the same line.
std::vector<double> location = {42.651443, -73.749302};
Another way we can initialize vector is by presizing, or setting the size. Example: initialize a vector with two elements:
std::vector<double> location(2);
Vector Index
An index refers to an element’s position within an ordered list. Vectors are 0-indexed, meaning the first element has index 0, the second index 1, and so on.
std::vector<char> vowels = {'a', 'e', 'i', 'o', 'u'};
- The character at index
0
is'a'
- The character at index
1
is'e'
- The character at index
2
is'i'
To output of the elements, we can do:
std::cout << vowels[0] << "\n";
std::cout << vowels[1] << "\n";
Using the notation vector[index]
with square brackets after the vector name and the element’s index number inside.
Adding and Removing Vector Elements
To add a new element to the “back”, or end of the vector, we can use the .push_back()
function.
std::vector<std::string> dna = {"ATG", "ACG"};
// Add elements:
dna.push_back("GTG");
dna.push_back("CTG");
We can also remove elements from the “back” of the vector using .pop_back()
.
dna.pop_back();
Vector size
The .size()
function returns the number of elements in the vector (and elements in the string).
std::vector<std::string> grocery = {"Hot Pepper Jam", "Dragon Fruit", "Brussel Sprouts"};
std::cout << grocery.size() << "\n"; // ==3
Change each of the values within a vector
for (int i = 0; i < vector.size(); i++) {
vector[i] = vector[i] + 10;
}
Functions
- You can build DRY (Don’t Repeat Yourself) code, reusing the code you already wrote
- Functions help make your code flexible and modular, meaning you can group your code more easily by task
A C++ function is comprised of two distinct parts:
- Declaration: this includes the function’s name, what the return type is, and any parameters (if the function will accept input values, known as arguments).
- Definition: also known as the body of the function, this contains the instructions for what the function is supposed to do.
return_type function_name( any, parameters, you, have ) {
// Code block here
return output_if_there_is_any;
}
bool is_even(int number) {
if (number % 2 == 0) {
return true;
}
return false;
}
Built-in Functions
We gain access to various functions by including headers like <cmath>
or <string>
.
sqrt()
to find the square root of any number. Example: sqrt(9) ;-
rand()
with the modulo operator to generate a random number between 0 and your favorite number. Example: number = rand() % 29; - pow(a, b) will raise
a
to the power ofb
// This seeds the random number generator:
srand (time(NULL));
// Use rand() below to initialize number
int number = rand() % 29;
std::cout << number;
Void Function
A void
function, also known as a subroutine, has no return value.
void hello() {
std::cout << "Hello World!\n";
}
void get_emergency_number(std::string emergency_number){
std::cout << "Dial " << emergency_number << "\n";
}
Return Types
A function can return most data types we’ve covered, including double
, int
, bool
, char
, std::string
, andstd::vector
.
- The return statement is the last line the function will execute
- There must be a value returned from the function
- The return value must be the same type as the function’s return type
Parameters
Parameters are variables used in a function’s definition. They act as placeholders for the input values you’ll use during your function call.
- The function call must include the same number of arguments as there are parameters.
- The corresponding arguments must be passed in the same order.
Declaring functions
With a few functions, you can declare the function above main()
and then you can define the function below main()
like this:
#include <iostream>
// Declaration at the top:
void eat();
int main() {
eat();
}
// Definition at the bottom:
void eat() {
std::cout << "nom nom nom\n";
}
To make your code cleaner and more modular, you can move the function definitions over to another specialized .cpp file (e.g., my_functions.cpp), leaving a list of declarations above main()
.
Before your program even compiles, it links together any files you list in your compilation statement into a single executable:
g++ main.cpp my_functions.cpp
Functions Default Arguments
If you add a parameter to a function in C++, then an argument will be required when you call the function. To make your code more flexible you can add default arguments to your function declarations. Default arguments are values assigned to parameters when the function is declared and defined:
// Declaration
void intro(std::string name, std::string lang = "C++");
// Definition
void intro(std::string name, std::string lang) {
std::cout << "Hi, my name is "
<< name
<< " and I'm learning "
<< lang
<< ".\n";
}
Then, if you leave the argument blank in your function call, your function will run with the default value. If you DO have an argument to add when you call the function, that argument will replace the default argument when your code executes.
Parameters without default arguments come first.
int add_nums(int num1, int num2 = 0);
Header files
You can move all function declarations over to a header file, another file — usually with the same name as the file with all of the function definitions — with the extension .hpp or .h. For example, if your function definitions are in my_functions.cpp, the corresponding header file would be my_functions.hpp or my_functions.h.
With headers, you can just add #include "my_functions.hpp"
to the very top of main.cpp:
#include "my_functions.hpp"
Inline functions
Term “inline functions” with a couple different meanings.
1.
An inline function is a function definition, usually in a header file, qualified by inline
like this:
inline
void eat() {
std::cout << "nom nom\n";
}
Using inline
advises the compiler to insert the function’s body where the function call is, which sometimes helps with execution speed (and sometimes hinders execution speed). If you do use it, we recommend testing how it affects the execution speed of your program.
2.
You will sometimes also hear about “inline functions” that are just member functions (i.e. functions inside of classes) which have been defined and declared in a single line in a header file because the function body is so short:
// cookie_functions.hpp
// eat() belongs to the Cookie class:
void Cookie::eat() {std::cout << "nom nom\n";}
Please note that you should ALWAYS add the inline
keyword if you are inlining functions in a header (unless you are dealing with member functions, which are automatically inlined for you).
Function overloading
In a process known as function overloading, you can give multiple C++ functions the same name. Just make sure at least one of these conditions is true:
- Each has different type parameters.
- Each has a different number of parameters.
Overloading enables you to change the way a function behaves depending on what is passed in as an argument:
void print_cat_ears(char let) {
std::cout << " " << let << " " << let << " " << "\n";
std::cout << let << let << let << " " << let << let << let << "\n";
}
void print_cat_ears(int num) {
std::cout << " " << num << " " << num << " " << "\n";
std::cout << num << num << num << " " << num << num << num << "\n";
}
Given the above functions, you could call the functions like so and C++ will know what to do:
print_cat_ears('A');
print_cat_ears(4);
Example:
int fancy_number(int num1, int num2);
int fancy_number(int num1, int num2, int num3);
int fancy_number(double num1, double num2);
Function Templates
When two functions have different types but the same body — as was the case with print_cat_ears()
—, there is a cleaner solution you can use: templates.
A template is a C++ tool that allows programmers to add data types as parameters.
This feature comes in handy for classes as well as for functions. In fact, std::string
and std::vector
are both template-based types.
Unlike regular functions, templates are entirely created in header files.
Templates let you choose the type implementation right when you call the function.
Here’s how we could build a template for print_cat_ears()
:
template <typename T>
void print_cat_ears(T item) {
std::cout << " " << item << " " << item << " " << "\n";
std::cout << item << item << item << " " << item << item << item << "\n";
}
We can call the function for int
, char
, std::string
, or double
.
If the template’s return type is flexible or depends on the implementation, it can be type T
like this:
template <typename T>
T get_smallest(T num1, T num2) {
return num2 < num1? num2 : num1;
}
Using templates will slow down the program’s compile time, but speed up the execution time.
Classes
The class serves as a blueprint for objects, which are instances of the class (just like age
is an instance of int
). An object gets characteristics and behaviors from its class.
We can create an empty C++ class like this in a header file:
class City {
}; // <-- notice this semicolon!
Components of a class are called class members. Just like you can get a string’s length using .length()
, you can access class members using the dot operator (object.class_member
).
There are two types of class members:
- Attributes, also known as member data, consist of information about an instance of the class.
- Methods, also known as member functions, are functions that you can use with an instance of the class.
class City {
// attribute
int population;
// we'll explain 'public' later
public:
// method
void add_resident() {
population++;
}
};
It’s common to declare methods inside the class (in a header), then define them outside the class (in a .cpp file of the same name). You can do this using ClassName::
before the method name to indicate its class like this:
int City::get_population() {
return population;
}
Unlike with regular functions, you will need to include
the header file in the .cpp file where you define the methods.
The method must also be declared inside the class if you want to define it outside.
Creating Objects
To create (or instantiate) an object, we can do this:
City accra;
We can give the object’s attributes values like this (note that these must be attributes you defined in the class):
accra.population = 2270000;
Later, we can access this information using the method we added to the City
class (if it’s in a public
part of the class):
accra.get_population();
Public and Private
By default, everything in a class is private
, meaning class members are limited to the scope of the class.
But sometimes you need access to class members, and for that there is public
. You can use it to make everything below accessible outside the class:
class City {
int population;
public: // stuff below is public
void add_resident() {
population++;
}
};
There is also a private
access modifier for when you want something below public
to be private to the class:
class City {
int population;
public:
void add_resident() {
population++;
}
private: // this stuff is private
bool is_capital;
};
Constructors
A constructor is a special kind of method that lets you decide how the objects of a class get created. It has the same name as the class and no return type. Constructors really shine when you want to instantiate an object with specific attributes.
If we want to make sure each City
is created with a name and a population, we can use parameters and a member initializer list to initialize attributes to values passed in:
// city.hpp
#include "city.hpp"
class City {
std::string name;
int population;
public:
City(std::string new_name, int new_pop);
};
// city.cpp
City::City(std::string new_name, int new_pop)
// members get initialized to values passed in
: name(new_name), population(new_pop) {}
You could also write the definition like this:
City::City(std::string new_name, int new_pop) {
name = new_name;
population = new_pop;
}
However, a member initialization list allows you to directly initialize each member when it gets created.
To instantiate an object with attributes, you can do:
// inside main()
City ankara("Ankara", 5445000);
Destructors
A destructor is a special method that handles object destruction. Like a constructor, it has the same name as the class and no return type, but is preceded by a ~
operator and takes no parameters:
City::~City() {
// any final cleanup
}
Object destruction is about tidying up and preventing memory leaks.
You generally won’t need to call a destructor; the destructor will be called automatically in any of the following scenarios:
- The object moves out of scope.
- The object is explicitly deleted.
- When the program ends.
Memory
Everything we put into memory has an address. For example, when we declare and initialize an int
variable named power
:
int power = 9000;
This will set aside an int
-size piece of memory for the variable power
somewhere and put the value 9000
into that memory.
References
In C++, a reference variable is an alias for something else, that is, another name for an already existing variable.
Suppose we have an int
variable already called songqiao
, we can create an alias to it by using the &
sign in the declaration:
int &sonny = songqiao;
So here, we made sonny
a reference to songqiao
.
Now when we make changes to sonny
(add 1, subtract 2, etc), songqiao
also changes.
- Anything we do to the reference also happens to the original.
- Aliases cannot be changed to alias something else.
Pass-By-Reference
When we passed parameters to a function, we used normal variables and that’s known as pass-by-value. But because the variables passed into the function are out of scope, we can’t actually modify the value of the arguments.
Pass-by-reference refers to passing parameters to a function by using references. When called, the function can modify the value of the arguments by using the reference passed in.
This allows us to:
- Modify the value of the function arguments.
- Avoid making copies of a variable/object for performance reasons.
The following code shows an example of pass-by-reference. The reference parameters are initialized with the actual arguments when the function is called:
void swap_num(int &i, int &j) {
int temp = i;
i = j;
j = temp;
}
int main() {
int a = 100;
int b = 200;
swap_num(a, b);
std::cout << "A is " << a << "\n";
std::cout << "B is " << b << "\n";
}
When swap_num()
is called, the values of the variables a
and b
will be modified because they are passed by reference. Notice that the int &i
and int &j
are the parameters of the function swap_num()
. When swap_num()
is called, the values of the variables a
and b
will be modified because they are passed by reference.
Suppose we didn’t pass-by-reference here and have the parameters as simply int i
and int j
in the swap_num()
function, then i
and j
would swap, but a
and b
wouldn’t be modified.
To reiterate, using references as parameters allows us to modify the the arguments’ values. This can be very useful in a lot cases.
Const
The const
keywords tells the compiler that we won’t change something. For example, in the following code, we are telling the compiler that the double
variable pi
will stay at 3.14
through out the program:
double const pi = 3.14;
If we try to change pi
, the compiler will throw an error.
Sometimes, we use const
in a function parameter; this is when we know for a fact that we want to write a function where the parameter won’t change inside the function. Here’s an example:
int triple(int const i) {
return i * 3;
}
In this example, we are not modifiying the i
. If inside the function triple()
, the value of i
is changed, there will be a compiler error.
So to save the computational cost for a function that doesn’t modify the parameter value(s), we can actually go a step further and use a const
reference:
int triple(int const &i) {
return i * 3;
}
This will ensure the same thing: the parameter won’t be changed. However, by making i
a reference to the argument, this saves the computational cost of making a copy of the argument.
Memory Address
We haved learned about references (aliases), which are created by using the &
symbol in a variable declaration. But the &
sign can have another meaning.
The “address of” operator, &
, is used to get the memory address, the location in the memory, of an object.
We can find where the variable is stored on the computer by printing out:
std::cout << &porcupine_count << "\n";
It will return something like:
0x7ffd7caa5b54
This is a memory address represented in hexadecimal. A memory address is usually denoted in hexadecimal instead of binary for readability and conciseness.
The double meaning of the &
symbol can be tricky at first, so make sure to note:
- When
&
is used in a declaration, it is a reference operator. - When
&
is not used in a declaration, it is an address operator.
Pointers
A pointer variable is mostly the same as other variables, which can store a piece of data. Unlike normal variables, which store a value (such as an int
, double
, char
), a pointer stores a memory address.
While references are a newer mechanism that originated in C++, pointers are an older mechnaism that was inherited from C. We recommend avoiding pointers as much as possible; usually, a reference will do the trick. However, you will see pointers a lot in the wild, particularly in older projects, where they are used in a very similar way to references.
Pointers must be declared before they can be used, just like a normal variable. They are syntactically distinguished by the *
, so that int*
means “pointer to int
“ and double*
means “pointer to double
“.
So suppose we have a variable called gum
:
int gum = 8;
We can create a pointer to it by:
int* ptr = &gum;
int*
makes it a pointer rather than a normal variable.ptr
is the pointer name.&gum
is the memory address of the other variablegum
.
Syntactically, spaces around *
do not matter, but the best practice is to have it after the data type.
int* number;
int *number;
int * number;
Dereference
Way to obtain the value pointed to by the pointer.
he asterisk sign *
a.k.a. the dereference operator is used to obtain the value pointed to by a variable. This can be done by preceding the name of a pointer variable with *
.
int blah = *ptr;
The double meaning of the *
symbol can be tricky at first, so make sure to note:
- When
*
is used in a declaration, it is creating a pointer. - When
*
is not used in a declaration, is is a dereference operator.
Null Pointer
When we declare a pointer variable like so, its content is not intialized:
int* ptr;
In other words, it contains an address of “somewhere”, which is of course not a valid location. This is dangerous! We need to initialize a pointer by assigning it a valid address.
But suppose we don’t know where we are pointing to, we can use a null pointer.
nullptr
is a new keyword introduced in C++11. It provides a typesafe pointer value repsenting an empty pointer.
We can use nullptr
like so:
int* ptr = nullptr;
In older C/C++ code, NULL
was used for this purpose. nullptr
is meant as a modern replacement to NULL
.
Manual Memory Management
To avoid wastage of memory, you can dynamically allocate any memory required during runtime using the new
and delete
operators in C++.
int main() {
// memory allocation
ptr = new int[num];
...
// memory is released
delete ptr;
}
If you create something with new
, at some point you must delete
it. When something is not deleted, it will cause a memory leak.
The Rule of Three
The rule of three is a rule of thumb in C++ that claims that if a class defines one of the following special member functions, it should define all three:
- Destructor
- Copy constructor
- Copy assignment operator
The Rule of Five
With C++11, a new rule emerged: the rule of five. This adds two more special functions to the rule of three list:
- Destructor
- Copy constructor
- Copy assignment operator
- Move constructor
- Move assignment operator
As you can see, manual memory management is difficult and old-fashioned. Instead of using new
and delete
, there’s something in the standard library that will make your life a lot easier
Standard Library Smart Pointers
A smart pointer is an abstract data type that was popularized by C++ during the early 1990’s. It simulates a pointer while providing additional features, such as automatic memory management.
In the standard library, we have the following:
unique_ptr
: a smart pointer that owns and manages another object through and disposes of that object when theunique_ptr
goes out of scope.shared_ptr
: a smart pointer that retains shared ownership of an object through a pointer. Severalshared_ptr
objects may own the same object.
// palindrome finder
#include <iostream>
// Define is_palindrome() here:
bool is_palindrome(std::string text) {
std::string reversed_text = "";
for (int i = text.size() - 1; i >= 0; i--) {
reversed_text += text[i];
}
if (reversed_text == text) {
return true;
}
return false;
}
int main() {
std::cout << is_palindrome("madam") << "\n";
std::cout << is_palindrome("ada") << "\n";
std::cout << is_palindrome("lovelace") << "\n";
}