C++ Advance Notes

Some C++ Advanced Usage Example Notes

一些个人C++的学习笔记

这篇post主要记录在写CS126代码时候,遇到的一些c++问题,留以记录。

首先强推这个Youtuber C++系列,非常直观+很有帮助:

https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw

这篇post主要围绕CS126 HW9 的一系列小问题展开

Pointers and References

You need to write a function swap two ints. You remember there are three ways to do it but unfortunately you can’t remember what the best way is. Why not try all three ways, then?

If a function is impossible to implement, return false. Otherwise, swap the numbers and return true.

Answer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool SwapByValue(int a, int b) {
return false;
}

bool SwapByPointer(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
return true;
}

bool SwapByReference(int &a, int &b) {
int temp = a;
a = b;
b = temp;
return true;
}

Explanation:

  1. Swap by Value 是无效的,因为两个参数都只是两份copy,原变量无任何变化。
  2. Swap by Pointer 原变量被两个指针指向并且传入参数,那么只需要Dereference后得到Alias,交换值后两个变量内存地址上的值就会被交换.
  3. Swap by Reference: Reference就是变量的Alias,改变直接反应到变量的内存地址上。

Iterators

You realize that Git uses linked lists internally, so you decide to implement your own version control system! This should be easy, right? Start by implementing the following iterator functions, so you can iterate over a list of commits.

When you’re done, your iterator should be able to be used in the following for loop:

1
2
3
4
5
6
7
8
9
CommitHistory history;
history.Push("First commit");
history.Push("Second commit");
// ...

for (CommitHistory::Iterator it = history.begin(); it != history.end(); ++it) {
std::string message = *it;
// ...
}

For reference, the header file is included below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma once

#include <string>


class CommitHistory {
public:
CommitHistory();
void Push(std::string message);

class Iterator {
public:
Iterator(Commit *curr);

Iterator& operator++();
std::string& operator*() const;
bool operator!=(const Iterator& other) const;

private:
Commit *curr_;
};

Iterator begin();
Iterator end();

private:
struct Commit {
std::string message_;
Commit *next_;
};

Commit *head_;
};

Hint: Each function should be roughly one line of code.

**Answer: **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "commit_history.h"


CommitHistory::CommitHistory() {
head_ = nullptr;
}

void CommitHistory::Push(std::string message) {
Commit *commit = new Commit();
commit->message_ = message;
commit->next_ = head_;

head_ = commit;
}

CommitHistory::Iterator::Iterator(Commit *curr) {
curr_ = curr;
}

bool CommitHistory::Iterator::operator!=(const Iterator& other) const {
return curr_ != other.curr_;
}

CommitHistory::Iterator& CommitHistory::Iterator::operator++() {
curr_ = curr_->next_;
return *this;
}

std::string& CommitHistory::Iterator::operator*() const {
std::string& message = curr_->message_;
return message;
}

CommitHistory::Iterator CommitHistory::begin() {
return CommitHistory::Iterator(head_);
}

CommitHistory::Iterator CommitHistory::end() {
return CommitHistory::Iterator(nullptr);
}

这里迭代器主要实现了6个method,分别是:

  1. Constructor CommitHistory::Iterator::Iterator(Commit *curr)
  2. operator!= bool CommitHistory::Iterator::operator!=(const Iterator& other) const
  3. operator++ CommitHistory::Iterator& CommitHistory::Iterator::operator++()
  4. dereference operator std::string& CommitHistory::Iterator::operator*() const
  5. begin() CommitHistory::Iterator CommitHistory::begin()
  6. end() CommitHistory::Iterator CommitHistory::end()

Templating

You’re writing a general-purpose geometry library, but… it’s not general enough! Use templating to add support for floats, longs, and other numeric types in the Rectangle class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#pragma once

template <typename T>
class Rectangle {
public:
Rectangle(T w, T h);
T CalculateArea();
T CalculatePerimeter();

private:
T w_;
T h_;
};
template <typename T>
Rectangle<T>::Rectangle(T w, T h) {
w_ = w;
h_ = h;
}
template <typename T>
T Rectangle<T>::CalculateArea() {
return w_ * h_;
}
template <typename T>
T Rectangle<T>::CalculatePerimeter() {
return 2 * w_ + 2 * h_;
}

Templating 的重点在于:在实现每个method 时候,不仅仅需要用上template, 同时需要放入对应的signature当中。

The Big 5

You want to write a backend program to keep track of this season’s Big 10 basketball games, so you can display live scores on your website. In your opinion, the only thing more exciting than the Big 10 conference is the Big 5 resource management functions, so you decide to implement them for the Team class.

  1. Destructor
  2. Copy Constructor
  3. Copy Assignment Operator
  4. Move Constructor
  5. Move Assignment Operator

For reference, the header file is included below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma once

#include <string>


class Team {
public:
Team();

~Team();
Team(const Team &other);
Team& operator=(const Team& other);
Team(Team&& other) noexcept;
Team& operator=(Team&& other) noexcept;

void EditLineup(size_t idx, std::string player);
void ShootFreeThrow();
void ShootTwo();
void ShootThree();

std::string* lineup();
int score();

private:
size_t kLineupSize = 5;

std::string *lineup_;
int score_;
};

Answer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include "team.h"

Team::Team() {
lineup_ = new std::string[kLineupSize];
score_ = 0;
}

Team::~Team() {
delete[] lineup_;
}

Team::Team(const Team& other) : score_(other.score_) {
lineup_ = new std::string[kLineupSize];
for (size_t i = 0; i < kLineupSize; ++i ){
lineup_[i] = other.lineup_[i];
}
}

Team& Team::operator=(const Team& other) {
if (this != &other){

delete[] lineup_;
score_ = other.score_;
lineup_ = new std::string[kLineupSize];
for (size_t i = 0; i < kLineupSize; ++i ){
lineup_[i] = other.lineup_[i];
}
}

return *this;
}

Team::Team(Team&& other) noexcept {
score_ = other.score_;
lineup_ = other.lineup_;

other.score_ = 0;
other.lineup_ = nullptr;

}

Team& Team::operator=(Team&& other) noexcept {

if (this != &other){
delete[] lineup_;

score_ = other.score_;
lineup_ = other.lineup_;

other.score_ = 0;
other.lineup_ = nullptr;
}

return *this;
}

void Team::EditLineup(size_t idx, std::string player) {
if (idx >= 0 && idx < kLineupSize) {
lineup_[idx] = player;
}
}

void Team::ShootFreeThrow() {
score_++;
}

void Team::ShootTwo() {
score_ += 2;
}

void Team::ShootThree() {
score_ += 3;
}

std::string* Team::lineup() {
return lineup_;
}

int Team::score() {
return score_;
}

Destructor

当destruct object时,destructor会被invoke,如果我们用了任何heap space,那么我们可以在destructor中调用delete 用来 free heap space

Copy Constructor

如果我们需要deep copy,可以自己定义copy constructor (默认的话是shallow copy)。

Copy Assignment Operator

在对已经分配空间后的对象上做assignment,和Copy Constructor 非常类似,但是需要check self-assignment 并且delete不需要的heap space。

Move Constructor

如果我们初始化对象的参数只是rvalue,即只是临时的,我们不需要copy一遍再初始化,而是直接move。记得把原来参数指向nullptr.

Move Assignment Operator

在已分配空间后的对象上做move assignment,和move constructor类似。

这里主要记录每一种对应的概念practice,供未来参考。对于概念我觉得开始推的那个油管主讲的非常好,如果看不明白这里的代码,强推他的c++系列视频!


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!