필자의 대학 코스 Advanced Programming에서 사용된 예시를 발췌, 정리했습니다. FINAL 시험에서 좋은 성과를 바라며…
POINT
Point라는 구조체는 x좌표와 y좌표로 정의될 수 있습니다.
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
#include <iostream>
#include <cmath> // for sqrt
struct point
{
double x;
double y;
};
double d(point const * p1, point const * p2) // 1
{
double x_diff = p1->x - p2->x; // 1
double y_diff = p1->y - p2->y; // 1
return sqrt(x_diff*x_diff + y_diff*y_diff);
}
int main()
{
using namespace std;
point p; // 2
cout << "x: " << flush; // 3
cin >> p.x; // 2
cout << "y: " << flush; // 3
cin >> p.y; // 2
point origin = {0, 0};
cout << "Distance: " << d(&p, &origin) << endl; // 1
return 0;
}
몇 가지 의미있는 내용이 있습니다.
- 함수 인자로 구조체의 주솟값을 받았다면, 구조체의 변수에 접근하기 위해서는
p1->x
형식으로, 즉[구조체 포인터]->[구조체의 변수]
형식으로 접근해야 합니다. - 그저 구조체를 받았다면, 구조체의 변수에 접근하기 위해
p.x
형식으로, 즉[구조체].[구조체의 변수]
형식으로 접근해야 합니다. - 제시문을 출력하고 사용자의 입력을 기다릴 때에는
<< flush
를 해주는 것이 관습입니다.
하지만 위 코드는 밑에처럼 쓸 수도 있습니다.
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
#include <iostream>
#include <cmath> // for sqrt
struct point
{
double x;
double y;
};
double d(point const & p1, point const & p2) // 1
{
double x_diff = p1.x - p2.x; // 1
double y_diff = p1.y - p2.y; // 1
return sqrt(x_diff*x_diff + y_diff*y_diff);
}
int main()
{
using namespace std;
point p;
cout << "x: " << flush;
cin >> p.x;
cout << "y: " << flush;
cin >> p.y;
point origin = {0, 0};
cout << "Distance: " << d(p, origin) << endl; // 1
return 0;
}
이번에는 함수의 인자를 구조체 자체로 주고 있습니다. 이 상황만 보면 누군가가 이렇게 비판하겠죠. “구조체를 인자로 전달하는 것은 구조체 전체를 복사하여 다시 연산하기 때문에 비효율적이야!” 하지만 위로 올라가서 함수의 인자 포맷을 보게 된다면, &
가 붙어 있는 것을 알 수 있습니다. 이는 *reference**를 활용한 값의 참조이며, 이 함수는 레퍼런스로 받은 인자들에 붙은 .
은 모두 ->
로 바꾸어서 처리하게 됩니다. 즉 1에서 바로 인자들에게 접근할 수 있는 것이지요. 잘 알아두시길 바랍니다.
후자의 방법이 메모리의 관점에서는 더 효율적입니다.
Pointer vs Reference
레퍼런스는 포인터와 중요한 차이점을 가집니다.
- 꼭 초기화를 시켜주지 않아도 되는 포인터와 달리, 레퍼런스는 반드시 처음에 누구의 별명이 될 것인지 지정해야 합니다.
- 가리키는 값이 바뀔 수 있는 포인터와 달리, 레퍼런스가 한 번 별명이 되면 절대로 다른 이의 별명이 될 수 없다.
- 항상 메모리상 공간을 차지하는 포인터와 달리, 레퍼런스는 메모리 상에 존재하지 않을 수도 있다.
조금 사소한 것들로는…
- 배열과 사용할 수 없다.
- 포인터 연산을 할 수 없다.
- null-reference는 불가능, null-pointer는 가능
- reference는 항상 무언가를 함의하도록 초기화되고, 그 값이 바뀌지 않는다.
여기 예시가 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
int main()
{
using namespace std;
int x[] = {3, 4, 5};
int &a = x[1];
a++;
cout << x[1] << endl;
int &b = a;
cout << b << endl;
return 0;
}
출력 결과
1
2
5
5
왜 이런 결과가 나왔을까요? int &a = x[1];
이 줄에서, a
는 x[1]
의 별명이 되었습니다. 즉 밑에 있는 a++
은 x[1]++
과 동치인 것이죠. 따라서 5가 출력됩니다.
또한, int &b = a;
에서 b
는 a
의 별명이 되었으므로 b
역시 x[1]
의 별명이 됩니다. 따라서 5가 출력됩니다.
ENUM
ENUM은 기존 정수형 변수를 조건을 벗어난 값으로 지정되지 않게 하고, 가독성을 높히기 위해 사용됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
enum day_of_week { SUN, MON, TUE, WED, THU, FRI, SAT };
string day_of_week_name(day_of_week d)
{
switch (d)
{
case SUN: return "SUN";
case MON: return "MON";
case TUE: return "TUE";
case WED: return "WED";
case THU: return "THU";
case FRI: return "FRI";
case SAT: return "SAT";
}
}
즉, enum
은 switch
문과 각별한 사이이다.
enum
은 내재적으로 int
형 변수로 타입이 전환되곤 한다. 또한 global range에서 정의되기 때문에, 우리는 다른 SUN
이란 변수를 만들 수 없다. 이를 막기 위해서, 혼동을 줄이기 위해서 enum class
로 정의할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
enum class day_of_week { SUN, MON, TUE, WED, THU, FRI, SAT };
string day_of_week_name(day_of_week d)
{
switch (d)
{
case day_of_week::SUN: return "SUN";
case day_of_week::MON: return "MON";
case day_of_week::TUE: return "TUE";
case day_of_week::WED: return "WED";
case day_of_week::THU: return "THU";
case day_of_week::FRI: return "FRI";
case day_of_week::SAT: return "SAT";
}
}
이제 헷갈리지 않는다!
ENUM으로 날짜 계산해주는 프로그램 만들기
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
#include <iostream>
enum class month {jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec};
int days_of_month(month m)
{
case month::jan:
case month::mar:
case month::may:
case month::jul:
case month::aug:
case month::oct:
case month::dec:
return 31;
case month::apr:
case month::jun:
case month::sep:
case month::nov:
return 30;
case month::feb:
return 28
}
struct day_of_month
{
month m;
int day;
}
int days_between (day_of_month const & d1, day_of_month const &d2)
{
int total_days;
for (month i = d1.m; i < d2.m; i = (month)((int)i + 1))
total_days += days_of_month(i);
return total_days + d2.day - d1.day;
}
int main()
{
using namespace std;
day_of_month a = {month::april, 30}, {month::may, 3};
cout << days_between(a, b) << endl;
}