여러 가지 class의 메모리 할당에 대해 알아보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <iostream>
using namespace std;
class Person{
public:
char name;
char address;
};
int main()
{
Person gildong;
cout << sizeof(gildong) << "\n"; //2
return 0;
}
|
cs |
위와 같은 경우엔 결과값이 주석에 표시한 것 같이 2가 나옵니다.
char가 1바이트이기 때문이죠.
하지만 멤버 변수에 int age를 추가하면 어떻게 될까요?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <iostream>
using namespace std;
class Person{
public:
char name;
int age;
char address;
};
int main()
{
Person gildong;
cout << sizeof(gildong) << "\n"; //12
return 0;
}
|
cs |
결과는 12가 나옵니다.???? 왜 12가 나오는 걸까요?
이유는 해당 클래스에 있는 자료형 기준으로 가장 큰 값을 기본단위로 해서 할당하기 때문입니다.
따라서 위의 메모리는 아래와 같이 되어 있습니다.
따라서 name 멤버 변수를 배열로 만든다면 어떻게 될까요?
char name; 을 char name [3];으로 바뀐다면 gildong의 전체 크기는 변함이 없습니다. (12byte)
하지만 메모리 구조는 다음과 같이 바뀝니다.
같은 원리로 name [5] 이상의 배열이 추가된다면 기본단위만큼(4byte) 증가해 16이 되겠죠?
멤버 함수도 추가해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
using namespace std;
class Person{
public:
char name;
int age;
char address;
void setHappy();
};
int main()
{
Person gildong;
cout << sizeof(gildong) << "\n"; //12
return 0;
}
|
cs |
멤버 함수를 추가하였지만 값은 바뀌지 크기는 변하지 않았습니다.
이유는 멤버 함수는 객체가 생성되면 객체 내에 존재하지만, 메모리에는 별도의 공간에 위치합니다.
이 함수가 정의된 클래스의 모든 객체가 이를 공유하는 형태를 취하죠.
따라서 객체의 메모리에는 변함이 없습니다.
하지만 가상 함수의 경우에는 어떻게 될까요?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <iostream>
using namespace std;
class Person{
public:
char name;
int age;
char address;
virtual void setHappy(){ };
};
int main()
{
Person gildong;
cout << sizeof(gildong) << "\n"; //16
return 0;
}
|
cs |
가상함수의 경우에는 16byte가 출력됩니다.???? 뭐져?
가상 함수의 경우에는 컴파일 시간에 바인딩(정적 바인딩)이 되지 않고 실행시간에 바인딩 (동적 바인딩)이 됩니다.
또한 가상 함수의 경우에는 vtable이 만들어지고 그 vtable 내의 어떤 함수가 실행될지를 나타낼 포인터가 필요합니다.
그 포인터의 공간(4byte)이 하나 더 생기는 것이죠.
따라서 16byte로 출력됩니다. 메모리의 구조는 아래와 같겠네요.
마지막으로 상속은 받은 클래스의 경우를 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include <iostream>
using namespace std;
class Person{
public:
char name;
int age;
char address;
virtual void getHappy(){ };
};
class Korean : public Person{
public:
int korean_age;
virtual void getHurry(){ };
};
int main()
{
Korean gildong;
cout << sizeof(gildong) << "\n"; //20
return 0;
}
|
cs |
왜 그런지 우선 메모리 구조를 먼저 보겠습니다.
여기서 virtual 함수들은 별도로 생성된 vtable에만 기록되기 때문에 할당받은 객체 크기는 늘어나지 않는 것을 볼 수 있습니다.
또한, 메모리에 생성되는 순서는 기본 클래스에서 파생 클래스 순서로 생성되는 것을 알 수 있습니다.
객체 생성 순서를 생각하시면 이해하는데 편할 것 같습니다.
번외
이 글을 쓰다가 virtual 멤버 함수의 주소 값을 출력하고 싶어서 알아본 내용을 공유합니다.
결국 주소를 출력해서 설명하는 건 힘들꺼같더라구욬ㅋㅋ
참고로 아래 코드는 gcc에서만 동작합니다.
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
|
#include <iostream>
using namespace std;
class Person1 {
public:
char name;
int age;
char address;
void setHappy(){ };
};
class Person2{
public:
char name;
int age;
char address;
virtual void setAngry(){ };
};
int main(){
Person1 *p1_p1 = new Person1();
Person1 *p1_p2 = new Person1();
Person2 *p2_p1 = new Person2();
Person2 *p2_p2 = new Person2();
void (Person1::*fp1)() = &Person1::setHappy;
void (Person2::*fp2)() = &Person2::setAngry;
printf("address: %p\n", (void*)(p1_p1->*fp1));
printf("address: %p\n", (void*)(p1_p2->*fp1));
printf("address: %p\n", (void*)(p2_p1->*fp2));
printf("address: %p\n", (void*)(p2_p2->*fp2));
return 0;
}
//result
//address: 0x55ca0ffaf240
//address: 0x55ca0ffaf240
//address: 0x55ca0ffaf250
//address: 0x55ca0ffaf250
|
cs |
https://stackoverflow.com/questions/3068144/print-address-of-virtual-member-function
https://stackoverflow.com/questions/28050325/illegal-operation-on-bound-member-function-expression
Reference
메모리 할당에 대해 좀 더 심화된 내용을 알고 싶은 분께 추천합니다.
궁금한 점이나 의견이 있으시면 댓글로 달아주세요!
'C,C++' 카테고리의 다른 글
[C, C++] type conversion 형 변환 (0) | 2022.05.03 |
---|---|
[C, C++] #include <헤더파일>과 #include "헤더파일"의 차이 (0) | 2019.10.23 |
[C++] 공백이 포함된 문자열 입력받기(char array, string : getline) (0) | 2019.10.23 |
[C++] 문법 char to string , string to char 변환 (0) | 2019.10.16 |
[C++, STL] <algorithm> std::fill 함수 사용하기 (feat. std::vector, 1차원 배열, 2차원 배열 초기화) (0) | 2019.09.25 |
댓글