씹어먹는 C++ Study (1)
참조자 (Reference)
C언어 에서 어떤 변수를 가르키고 싶을때 포인터로 주소값을 가져와 사용함. C++에서는 참조자 (Reference)를 사용하여 포인터처럼 사용할 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
int change_val_pointer(int *p) {
*p = 3;
return 0;
}
int change_val_reference(int &p) {
p = 5;
return 0;
}
int main(){
int num = 5;
change_val_pointer(&num);
change_val_reference(p);
}
1
2
3
4
int a = 5;
int& a_ref = a;
int& b; // 이런식으로 선언하면 컴파일러 오류
- 레퍼런스는 반드시 처음 선언될 때 누구를 참조할 것인지 지정해야 됨
1
2
3
4
5
int a = 5;
int& ref = a;
int b =3;
ref = b; // 불가능
- 레퍼런스는 한번 선언되면 참조를 변경할 수 없음
- 레퍼런스는 포인터와 다르게 메모리 상에 존재하지 않을 경우가 많다.
- 윈도우에서 바로가기라고 생각하면 편함
1
2
3
int a = 5;
int& ref1 = a;
int& ref2 = ref1; // ref1, ref2 모두 a를 가르킴
- 참조의 참조는 불가능 하지만 참조를 복사하는건 가능
- 윈도우에서 바로가기(1) , 바로가기(2) 처럼 모두 하나를 가르키는것처럼 여러개의 바로가기가 생성되는것과 같음
1
2
3
4
int &ref = 4 //상수는 literal이기때문에 컴파일 오류
const int &ref = 4 // 상수 참조자로 선언하면 가능함
int a = ref; // a = 4 문장과 동일함
레퍼런스의 배열
1
2
3
int a = 1;
int b = 2;
int & arr[2] = {a, b};
- 문법상 배열의 이름은 첫 번째 원소의 주소값으로 변환되기 떄문에 주소값이 존재한다는 의미는 메모리 상에 존재하게 되기 떄문에 문법 오류가 된다.
배열의 레퍼런스
1
2
int arr[3] = {1,2,3};
int(&ref)[3] = arr;
- [ ]의 연산 우선순위가 &보다 높기 때문에 괄호가 없으면 int &(ref[3])과 같이 선언된다.
함수와의 관계
1
2
3
4
5
6
7
int func(){
int a = 2;
return a;
}
int main(){
int b = func();
}
- 함수 내부의 지역 변수 a의 값은 b에 복사됨(새로운 메모리 공간에 할당). 이때 리턴되는 객체의 크기가 크면 복사되는데 시간이 오래 걸리므로 객체의 레퍼런스를 리턴하는 것이 효율적이다.
1
2
3
4
5
6
7
int& func(){
int a = 2;
return a;
}
int main(){
int b = func();
}
- 지역 변수의 레퍼런스가 리턴되는 경우 레퍼런스는 남고 원본인 a는 함수 리턴과 동시에 삭제되므로 Dangling Reference가 된다.
1
2
3
4
5
6
7
8
9
10
int& func(int& a){
a = 5;
return a;
}
int main(){
int b = 2;
int c = func(b); // 새로운 메모리에 5가 할당됨
int& d = func(b); // b의 주소값이 복사됨
}
- 외부 변수를 받아 리턴되는 경우 주소값의 복사로 전달이 끝남
new, delete
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
int func(int arr_size){
int *list = new int[arr_size];
for(int i = 0; i < arr_size; i++{
list[i] = i;
}
delete[] list;
}
typedef struct Animal{
char name[30];
int age;
} Animal;
int func2(int size) {
Animal *animal_list;
for(int i = 0; i < size; i++){
animal_list[i] = new Animal;
}
// delete
for(int i=0; i < size; i++){
delete animal_list[i];
}
}
객체 (Object)
추상화(abstraction), 인스턴스 변수(instance variable), 인스턴스 메소드(instance method), 캡슐화(Encapsulation), class, member variable, member function
class는 객체의 설계도, 객체는 instance class
Object 챕터 문제 해결
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
class Date {
int year_;
int month_;
int day_;
int m_day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public:
void SetDate(int year, int month, int date){
year_ = year;
month_ = month;
day_ = date;
if (year_ % 4 == 0) {
m_day[1] = 29;
}
else {
m_day[1] = 28;
}
}
void AddDay(int inc) {
day_ = day_ + inc;
while (day_ > m_day[month_ - 1]) {
day_ = day_ - m_day[month_ - 1];
AddMonth(1);
}
}
void AddMonth(int inc) {
month_ = month_ + inc;
while (month_ > 12) {
month_ = month_ - 12;
AddYear(1);
}
}
void AddYear(int inc) {
year_ = year_ + inc;
if (year_ % 4 == 0) {
m_day[1] = 29;
}
else {
m_day[1] = 28;
}
}
void ShowDate(){
printf("%d년 %d월 %d일\n", year_, month_, day_);
}
};
함수의 오버로딩, 생성자
- C++에서 같은 이름의 함수를 호출했을때 사용하는 인자(Parameter)를 보고 구별한다.
C++ 컴파일러에서 함수를 오버로딩하는 과정
1단계
자신과 타입이 정확히 일치하는 함수를 찾는다.
2단계
1단계에서 정확히 일치하는 타입이 없을떄는 아래의 형변환 규칙을 통해 일치하는 함수를 찾는다.
Char
,unsigned char
,short
,short
=>int
unsigned short
=>int
orunsigned int
Float
=>double
Enum
=>int
3단계
좀더 포괄적인 형변환을 통해 일치하는 함수를 찾는다.
- 임의의 숫자(numeric),
Enum
타입은 다른 숫자타입으로 변환(예를들어float
->int
) - Null은 포인터 타입이나 숫자타입인 0으로 변환된다.
- 포인터는
void
포인터로 변환된다.
4단계
유저 정의된 타입 변환으로 일치하는 것을 찾는다.
1
2
3
4
5
6
7
8
void print(int x) {...}
void print(char x) {...}
int main(){
double a = 3.2f;
print(a);
}
- 이 경우 3단계에서
double
은char
와int
2개로 변환 될 수 있는데, 같은 단계에서 일치하는 함수가 2개이상이면 모호하기 떄문에 컴파일러에서 오류가 발생한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Date {
int year_;
int month_;
int day_;
Date() { // default construct
year_ = 2012;
month_ = 7;
day_ = 12;
}
Date(int year, int month, int day){ // construct
year_= year;
month_ = month;
day_ = day;
}
...
}
Leave a comment