0% found this document useful (0 votes)
6 views

lecture3_initandref

Uploaded by

omarmatin2024
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

lecture3_initandref

Uploaded by

omarmatin2024
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 76

Initialization &

References
Fun times!

1
Today
- Initialization
- Using auto
- References
- If time: Const

11
Definition

Initialization: How we
provide initial
values to variables

12
Reminder: Structs in Code
struct Student {
string name; // these are called fields
string state; // separate these by semicolons
int age;
};

Student s;
s.name = "Sarah";
s.state = "CA";
s.age = 21; // use . to access fields
13
Recall: Two ways to initialize a struct
Student s; // initialization after we declare
s.name = "Sarah";
s.state = "CA";
s.age = 21;
//is the same as ...
Student s = {"Sarah", "CA", 21};
// initialization while we declare

14
Multiple ways to initialize a pair...
std::pair<int, string> numSuffix1 = {1,"st"};
std::pair<int, string> numSuffix2;
numSuffix2.first = 2;
numSuffix2.second = "nd";
std::pair<int, string> numSuffix2 =
std::make_pair(3, "rd");
15
Definition

Uniform initialization: curly


bracket initialization. Available
for all types, immediate
initialization on declaration!

16
Uniform Initialization
std::vector<int> vec{1,3,5};
std::pair<int, string> numSuffix1{1,"st"};
Student s{"Sarah", "CA", 21};
// less common/nice for primitive types, but
possible!
int x{5};
string f{"Sarah"};

17
Careful with Vector initialization!
std::vector<int> vec1(3,5);
// makes {5, 5, 5}, not {3, 5}!
// uses a std::initializer_list (more later)
std::vector<int> vec2{3,5};
// makes {3, 5}

18
TLDR: use uniform
initialization to initialize
every field of your
non-primitive typed
variables - but be careful not
to use vec(n, k)!
19
Questions?

20
Today
- Initialization
- Using auto
- References
- If time: Const

21
Recap: Type Deduction with auto

22
Definition

auto: Keyword used in


lieu of type when
declaring a variable, tells
the compiler to deduce
the type.
23
Type Deduction using auto
// What types are these?
auto a = 3;
auto b = 4.3;
auto c = ‘X’;
auto d = “Hello”;
auto e = std::make_pair(3, “Hello”);

📝 auto does not mean that the variable doesn’t have a type.
It means that the type is deduced by the compiler.
24
Type Deduction using auto
// What types are these?
auto a = 3; // int
auto b = 4.3; // double
auto c = ‘X’; // char
auto d = “Hello”; // char* (a C string)
auto e = std::make_pair(3, “Hello”);
// std::pair<int, char*>
📝 auto does not mean that the variable doesn’t have a type.
It means that the type is deduced by the compiler.
25
‼ auto does not mean that
the variable doesn’t have a
type.
It means that the type is
deduced by the compiler.
26
When should we use auto?

27
Code Demo Recap!
quadratic.cpp

28
Radical
If Radical < 0, no real
roots

29
Quadratic: Typing these types out is a pain...
int main() {
int a, b, c;
std::cin >> a >> b >> c;
std::pair<bool, std::pair<double, double>> result =
quadratic(a, b, c);
bool found = result.first;
if (found) {
std::pair<double, double> solutions = result.second;
std::cout << solutions.first << solutions.second << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}
30
Quadratic: Typing these types out is a pain...
int main() {
int a, b, c;
std::cin >> a >> b >> c; Cleaner! 🧼
auto result = quadratic(a, b, c);
bool found = result.first;
😊
if (found) {
auto solutions = result.second;
std::cout << solutions.first << solutions.second << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}

31
Don’t overuse auto

32
Don’t overuse auto!
int main() {
auto a, b, c;
std::cin >> a >> b >> c;
auto result = quadratic(a, b, c);
bool found = result.first;
if (found) {
auto solutions = result.second;
std::cout << solutions.first << solutions.second << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}

33
Can’t deduce the type b/c no value provided
int main() {
auto a, b, c; //compile error! ERROR!
std::cin >> a >> b >> c;
auto result = quadratic(a, b, c);
bool found = result.first;
if (found) {
auto solutions = result.second;
std::cout << solutions.first << solutions.second << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}

34
For simple types (like bool) type it out
int main() {
int a, b, c; LESS CLEAR 👎☹
std::cin >> a >> b >> c;
auto result = quadratic(a, b, c);
auto found = result.first; //code less clear :/
if (found) {
auto solutions = result.second;
std::cout << solutions.first << solutions.second << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}

35
Don’t overuse auto

...but use it to reduce long type names

36
Questions?

37
Structured Binding

38
Structured binding lets you initialize directly from
the contents of a struct
Before After
auto p = auto p =
std::make_pair(“s”, 5); std::make_pair(“s”, 5);
string a = p.first; auto [a, b] = p;
int b = p.second; // a is string, b is int
// auto [a, b] =
std::make_pair(...);

📝 This works for regular structs, too. Also, no nested structured binding. 39
A better way to use quadratic…
int main() {
int a, b, c;
std::cin >> a >> b >> c;
auto result = quadratic(a, b, c);
bool found = result.first;
if (found) {
auto solutions = result.second;
std::cout << solutions.first << solutions.second << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}

40
Using Structured Binding
int main() {
int a, b, c;
std::cin >> a >> b >> c;
auto [found, solutions] = quadratic(a, b, c);
if (found) {
auto [x1, x2] = solutions;
std::cout << x1 << “ ” << x2 << endl;
} else {
std::cout << “No solutions found!” << endl;
}
}

📝 This is better is because it’s semantically clearer: variables have clear names.
41
Questions?

42
Today
- Initialization
- Using auto
- References
- If time: Const

43
Definition

Reference: An alias
(another name) for a
named variable

44
void changeX(int& x){ // changes to x will persist
x = 0;
}
void keepX(int x){
x = 0;
}
int a = 100;
int b = 100;
changeX(a); // a becomes a reference to x
keepX(b); // b becomes a copy of x
cout << a << endl; //0
cout << b << endl; //100
45
Standard C++ vector (intro)

46
Standard std::vector
std::vector<int> v;
std::vector<int> v(n, k);
v.push_back(k);
v[i] = k;
int k = v[i];

v.empty();
v.size();
v.clear();

47
References to variables
vector<int> original{1, 2};
vector<int> copy = original;
vector<int>& ref = original;
original.push_back(3);
copy.push_back(4);
ref.push_back(5);

cout << original << endl;


cout << copy << endl;
cout << ref << endl;
48
References to variables
vector<int> original{1, 2};
vector<int> copy = original;
vector<int>& ref = original;
original.push_back(3);
copy.push_back(4);
ref.push_back(5);

cout << original << endl; // {1, 2, 3, 5}


cout << copy << endl;
cout << ref << endl;
49
References to variables
vector<int> original{1, 2};
vector<int> copy = original;
vector<int>& ref = original;
original.push_back(3);
copy.push_back(4);
ref.push_back(5);

cout << original << endl; // {1, 2, 3, 5}


cout << copy << endl; // {1, 2, 4}
cout << ref << endl;
50
References to variables
vector<int> original{1, 2};
vector<int> copy = original;
vector<int>& ref = original;
original.push_back(3);
copy.push_back(4);
ref.push_back(5);

cout << original << endl; // {1, 2, 3, 5}


cout << copy << endl; // {1, 2, 4}
cout << ref << endl; // {1, 2, 3, 5}
51
References to variables
vector<int> original{1, 2};
vector<int> copy = original; “=” automatically makes
vector<int>& ref = original; a copy! Must use & to
original.push_back(3); avoid this.
copy.push_back(4);
ref.push_back(5);

cout << original << endl; // {1, 2, 3, 5}


cout << copy << endl; // {1, 2, 4}
cout << ref << endl; // {1, 2, 3, 5}
52
The classic reference-copy bug 1.0:
void shift(vector<std::pair<int, int>>& nums) {
for (size_t i = 0; i < nums.size(); ++i) {
auto [num1, num2] = nums[i];
num1++;
num2++;
}
}

53
The classic reference-copy bug 1.0:
void shift(vector<std::pair<int, int>>& nums) {
for (size_t i = 0; i < nums.size(); ++i) {
auto [num1, num2] = nums[i];
num1++; ++i: increment then return
size_t is commonly used for
i++: return then increment
indicesnum2++;
because it’s unsigned
In for loops, both work the
and dynamically sized (using
} same and no performance
sizeof()). Nitty gritty
difference anymore so use
} what you prefer! Nitty gritty

54
The classic reference-copy bug 1.0:
void shift(vector<std::pair<int, int>>& nums) {
for (size_t i = 0; i < nums.size(); ++i) {
auto [num1, num2] = nums[i];
num1++;
num2++;
2 min: THINK, PAIR, SHARE!
}
}

55
The classic reference-copy bug 1.0:
void shift(vector<std::pair<int, int>>& nums) {
for (size_t i = 0; i < nums.size(); ++i) {
auto [num1, num2] = nums[i];
num1++;
num2++;
} This creates a copy of the
} course
This is updating that same
copy!

56
The classic reference-copy bug 1.0: Fixed
void shift(vector<std::pair<int, int>>& nums) {
for (size_t i = 0; i < nums.size(); ++i) {
auto& [num1, num2] = nums[i];
num1++;
num2++;
}
}

57
The classic reference-copy bug 2.0:
void shift(vector<std::pair<int, int>>& nums) {
for (auto [num1, num2]: nums) {
num1++;
num2++;
}
}

58
The classic reference-copy bug 2.0:
void shift(vector<std::pair<int, int>>& nums) {
for (auto [num1, num2]: nums) {
num1++;
num2++;
}
This creates a copy of the
} course
This is updating that same
copy!

59
The classic reference-copy bug 2.0, fixed:
void shift(vector<std::pair<int, int>>& nums) {
for (auto& [num1, num2]: nums) {
num1++;
num2++;
}
}

60
Definition: l-values vs r-values
- l-values can appear on the
left or right of an =
- x is an l-value

int x = 3;
int y = x;

l-values have names


l-values are not
temporary
61
Definition: l-values vs r-values
- l-values can appear on the - r-values can ONLY appear on
left or right of an = the right of an =
- x is an l-value - 3 is an r-value

int x = 3; int x = 3;
int y = x; int y = x;

l-values have names


r-values don’t have names
l-values are not
r-values are temporary
temporary
62
The classic reference-rvalue error
void shift(vector<std::pair<int, int>>& nums) {
for (auto& [num1, num2]: nums) {
num1++;
num2++;
}
}

shift({{1, 1}});

63
The classic reference-rvalue error
void shift(vector<std::pair<int, int>>& nums) {
for (auto& [num1, num2]: nums) {
num1++;
num2++;
}
}

shift({{1, 1}});
// {{1, 1}} is an rvalue, it can’t be referenced
64
The classic reference-rvalue error, fixed
void shift(vector<pair<int, int>>& nums) {
for (auto& [num1, num2]: nums) {
num1++;
num2++;
}
}
auto my_nums = {{1, 1}};
shift(my_nums);

65
Note: You can only create references to variables

int& thisWontWork = 5; // This doesn't work!

66
Questions?

67
Today
- Initialization
- Using auto
- References
- If time: Const

68
BONUS: Const and Const References

69
const indicates a variable can’t be modified!
const variables can be references or not!

std::vector<int> vec{1, 2, 3};


const std::vector<int> c_vec{7, 8}; // a const variable
std::vector<int>& ref = vec; // a regular reference
const std::vector<int>& c_ref = vec; // a const reference

vec.push_back(3);
c_vec.push_back(3);
ref.push_back(3);
c_ref.push_back(3);
70
const indicates a variable can’t be modified!
const variables can be references or not!

std::vector<int> vec{1, 2, 3};


const std::vector<int> c_vec{7, 8}; // a const variable
std::vector<int>& ref = vec; // a regular reference
const std::vector<int>& c_ref = vec; // a const reference

vec.push_back(3); // OKAY
c_vec.push_back(3);
ref.push_back(3);
c_ref.push_back(3);
71
const indicates a variable can’t be modified!
const variables can be references or not!

std::vector<int> vec{1, 2, 3};


const std::vector<int> c_vec{7, 8}; // a const variable
std::vector<int>& ref = vec; // a regular reference
const std::vector<int>& c_ref = vec; // a const reference

vec.push_back(3); // OKAY
c_vec.push_back(3); // BAD - const
ref.push_back(3);
c_ref.push_back(3);
72
const indicates a variable can’t be modified!
const variables can be references or not!

std::vector<int> vec{1, 2, 3};


const std::vector<int> c_vec{7, 8}; // a const variable
std::vector<int>& ref = vec; // a regular reference
const std::vector<int>& c_ref = vec; // a const reference

vec.push_back(3); // OKAY
c_vec.push_back(3); // BAD - const
ref.push_back(3); // OKAY
c_ref.push_back(3);
73
const indicates a variable can’t be modified!
const variables can be references or not!

std::vector<int> vec{1, 2, 3};


const std::vector<int> c_vec{7, 8}; // a const variable
std::vector<int>& ref = vec; // a regular reference
const std::vector<int>& c_ref = vec; // a const reference

vec.push_back(3); // OKAY
c_vec.push_back(3); // BAD - const
ref.push_back(3); // OKAY
c_ref.push_back(3); // BAD - const
74
Can’t declare non-const reference to const variable!
const std::vector<int> c_vec{7, 8}; // a const variable

// BAD - can't declare non-const ref to const vector


std::vector<int>& bad_ref = c_vec;

75
Can’t declare non-const reference to const variable!
const std::vector<int> c_vec{7, 8}; // a const variable

// fixed
const std::vector<int>& bad_ref = c_vec;

76
Can’t declare non-const reference to const variable!
const std::vector<int> c_vec{7, 8}; // a const variable

// fixed
const std::vector<int>& bad_ref = c_vec;

// BAD - Can't declare a non-const reference as equal


// to a const reference!
std::vector<int>& ref = c_ref;

77
const & subtleties
std::vector<int> vec{1, 2, 3};
const std::vector<int> c_vec{7, 8};

std::vector<int>& ref = vec;


const std::vector<int>& c_ref = vec;

auto copy = c_ref; // a non-const copy


const auto copy = c_ref; // a const copy
auto& a_ref = ref; // a non-const reference
const auto& c_aref = ref; // a const reference

78
Remember: C++, by default, makes
copies when we do variable
assignment! We need to use & if we
need references instead.

79
When do we use references/const references?
- If we’re working with a variable that takes up little
space in memory (e.g. int, double), we don’t need to
use a reference and can just copy the variable
- If we need to alias the variable to modify it, we can
use references
- If we don’t need to modify the variable, but it’s a big
variable (e.g. std::vector), we can use const references

80
You can return references as well!
// Note that the parameter must be a non-const reference to return
// a non-const reference to one of its elements!
int& front(std::vector<int>& vec) {
// assuming vec.size() > 0
return vec[0];
}

int main() {
std::vector<int> numbers{1, 2, 3};
front(numbers) = 4; // vec = {4, 2, 3}
return 0;
}
81
Can also return const references
const int& front(std::vector<int>& vec) {
// assuming vec.size() > 0
return vec[0];
}

82
Questions?

83
Recap:
- Uniform Initialization
- A “uniform” way to initialize variables of
different types!
- References
- Allow us to alias variables
- Const
- Allow us to specify that a variable can’t be
modified
84
Thanks for coming!
Next time: Streams!

85

You might also like