lecture3_initandref
lecture3_initandref
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
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 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
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);
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;
int x = 3; int x = 3;
int y = x; int y = x;
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
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!
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!
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!
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!
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!
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
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;
77
const & subtleties
std::vector<int> vec{1, 2, 3};
const std::vector<int> c_vec{7, 8};
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