With everything we've learned about pointers, we no longer need to create so many global variables.
For example, we can create a variable in one function, and give another function a pointer to access and modify it:
1void Combat(Character* A, Character* B) {
2 (*A).TakeDamage(50);
3 (*B).TakeDamage(50);
4}
5
6int main() {
7 Character Player;
8 Monster Monster;
9 Combat(&Player, &Monster);
10}
11
In the above example, it is worth talking about Line 2 in a bit more detail: (*A).TakeDamage(50);
Specifically, why have we wrapped the *A
in brackets?
The reason we have added brackets here is to control the order of operations.
Had we written *A.TakeDamage(50)
, our intentions might be clear to us - dereference A
then call TakeDamage
. But it could also be interpreted as call A.TakeDamage
then try to dereference the result.
If we put this code into our editor, it would become clear by the error, that the compiler is trying to do the latter. This is due to rules around operator precedence.
In maths classes, we may have learnt that different operators have different precedences. We don't always do things left-to-right.
For example, multiplication happens before addition. Therefore 1 + 2 * 3
is equal to 7
, not 9
;
This concept also applies to operators in programming languages.
The member access .
operator has higher precedence than the dereferencing operator *
in C++. So, when we have a statement that combines both operations, .
will happen before *
.
When we had the statement *A.TakeDamage(50)
, it was equivalent to *(A.TakeDamage(50))
. With A
being a pointer - we can't use the .
operator on it, so the compiler reported the error.
It's not important to know the precedence of all the operators. Even among professional developers, very few will have commited that to memory, because it's so easy to look up when it's needed.
The important thing to know is the concept: operators have different precedences, and we can add brackets to control it.
That being said, the precedence of all operators in C++ is given here: https://en.cppreference.com/w/cpp/language/operator_precedence
Reviewing the table at that website explains a quirk we saw the the previous lesson, where we had the code snippets (*x)++;
and *x *= 2;
Why did the first statement need (
and )
but the second one didn't? It was because *
has lower precedence than ++
, but higher precedence than *=
.
->
In the previous example, where A
is a pointer to an object, and we needed to access a member of that object, we used this pattern:
(*A).TakeDamage(50);
C++ does have an alternative to this, called the arrow operator:
A->TakeDamage(50);
This is much more common, therefore, it will be our preferred approach going forward.
Note, we only use the ->
operator with pointers to an object. When we have the actual object, or a reference to it, the .
operator must still be used to access its members.
// We use . with objects
Character MyCharacter;
MyCharacter.TakeDamage(50);
// We use . with references
Character& Reference { MyCharacter };
Reference.TakeDamage(50);
// We use -> with pointers
Character* Pointer { &MyCharacter };
Pointer->TakeDamage(50);
Given the following code, what could we put on line 7 to call the Equip
function of the Weapon
passed in as a parameter?
1class Weapon {
2public:
3 void Equip() {}
4};
5
6void PrepareForBattle(Weapon SelectedWeapon) {
7 // ???
8}
9
Given the following code, what could we put on line 7 to call the Equip
function of the Weapon
passed in as a parameter?
1class Weapon {
2public:
3 void Equip() {}
4};
5
6void PrepareForBattle(Weapon& SelectedWeapon) {
7 // ??
8}
9
Given the following code, what could we put on line 7 to call the Equip
function of the Weapon
passed in as a parameter?
1class Weapon {
2public:
3 void Equip() {}
4};
5
6void PrepareForBattle(Weapon* SelectedWeapon) {
7 // ??
8}
9
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way