본문 바로가기
C,C++

[C++] class의 메모리 할당방법 (virtual 멤버함수, 일반멤버함수, 상속)

by matters_ 2019. 11. 5.

여러 가지 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
 
위의 경우에는 20byte가 나오네요.

왜 그런지 우선 메모리 구조를 먼저 보겠습니다.

여기서 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

 

 

Print address of virtual member function

I am trying to print the address of a virtual member function. If I know which class implements the function I can write: print("address: %p", &A::func); But I want to do something like this:...

stackoverflow.com

https://stackoverflow.com/questions/28050325/illegal-operation-on-bound-member-function-expression

 

 

'&' : illegal operation on bound member function expression

This works when I try from a single cpp file with a main function, sprintf(smem_options , "#transcode{vcodec=RV24}:smem{" "video-prerender-callback=%lld," "no-time-sync}," , (l...

stackoverflow.com

 

 


Reference

메모리 할당에 대해 좀 더 심화된 내용을 알고 싶은 분께 추천합니다.

 

 

궁금한 점이나 의견이 있으시면 댓글로 달아주세요!

댓글