728x90
1. 개요
오늘은 신기한 포스터를 봐서 개시해볼려고 합니다.
2. 이게 왜 됨?
class Parent
{
public:
int age;
void run()
{
std::cout << "자식 run" << std::endl;
}
virtual void run2()
{
std::cout << "자식 run" << std::endl;
}
};
int main(void)
{
// case 1 non-virtual 함수 : 실행됨
Parent* temp = NULL;
temp->run();
// case 2 virtual 함수 : segfualt
Parent* temp2 = NULL;
temp2->run2();
// case3 멤버변수 : segfault
Parent* temp3 = NULL;
std::cout << temp3->age << std::endl;
return 0;
}
클래스의 맴버 변수와 맴버 함수는 객체가 할당될 때 메모리에 올라가기 때문에, 할당 후 접근이 가능합니다. 이걸 보고 난뒤에는 예상을 못하겠습니다.
그래서인지 nullptr로 접근한 case 2 나 case 3는 어느정도 납득이 갑니다.
근데 왜 case1은 이렇게 될까요?
이를 확인하기 위해 클래스의 메모리 구조에 대한 이해가 필요합니다.(static, virtual, binding 등 키워드도 알아야합니다.)
3. 메모리 구조
1. 클래스의 멤버
클래스에는 멤버변수와 멤버함수가 있고 각각 정적(static), 비정적(non-static)으로 선언이 가능하다.
또한 멤버함수는 가상(virtiual)으로 선언이 가능한데, virtual과 static은 동시 사용이 불가능하다.
표로 정리하면 아래와 같다.
C++ 클래스 | static | non-static | |
멤버변수 | static | non-static | |
멤버함수 | static | non-static | non-static + virtual |
2. static, virtual, binding은 간단하게 정리
static 멤버를 사용하는 이유
- static 멤버변수: 여러 인스턴스가 공유해서 사용할 수 있는 변수
- static 멤버함수: 인스턴스가 생성되지 않더라도 호출한 필요한 함수
가상함수를 사용하는 이유
: 동적바인딩을 통한 다형성 - 인스턴스의 실제 클래스에 따라 오버라이딩된 함수 호출이 가능
3. 클래스의 메모리 구조
클래스 안에 멤버변수와 멤버함수가 있기 때문에 둘이 같은 메모리 공간에 위치한다고 생각할 수 있지만, 그렇지 않다
- 인스턴스: 생성될 때마다 각각 다른 메모리 주소에 할당된다. (정적할당:스택 or 동적할당:힙)
- non-static 변수: 인스턴스의 생성과 동시에 할당된다. (정적할당: 스택 or 동적할당: 힙)
- static 변수: 인스턴스 생성 시가 아니라, 프로그램 시작 시 저장되고 프로그램이 종료되어야 소멸한다. (데이터영역) -> 여러 인스턴스가 공유한다.
- 멤버함수: static, non-static 모두 프로그램 시작 시 코드영역에 저장된다. 멤버함수는 인스턴스마다 다르게 동작하는 것이 아니기 때문에 인스턴스마다 함수가 할당되면 비효율적일 것이다. 따라서 해당 클래스의 모든 인스턴스는 코드 영역에 있는 멤버함수를 공유하면서 사용한다. (코드영역)
- 함수를 공유할 때, 함수를 호출한 인스턴스의 구분은 어떻게 하는 것일까? (ex 함수 내부에서 멤버변수를 사용한다면, 해당 인스턴스 정보가 필요) 멤버 함수가 코드영역에 올라갈 때 객체의 포인터를 hidden 인자로 전달받도록 변경되기 때문에, 어느 인스턴스에서 호출한 함수인지 확인할 수 있다.
- 클래스에 가상함수를 선언하면 가상함수테이블이 생성되고(어디에? 아마 코드영역..??) 클래스 내부적으로 가상함수테이블을 가리키는 포인터를 갖는다. (4 or 8바이트)
- 가상함수의 개수에 관계없이 포인터의 개수는 1개고, 클래스의 크기에 영향을 준다.
- 가상함수테이블은 클래스단위로 생성되고, 가상함수테이블 포인터는 인스턴스단위로 생성된다.
4. 요약
- 맴버 함수는 코드 영역에 저장이된다.
- 가상 함수 테이블은 코드 영역에 저장되지만 그걸 가르키는 가상함수 포인터가 인스턴스화를 안했기때문에 segfault가 뜬다
5. Ref
'개발자 면접 공부 > C-C++' 카테고리의 다른 글
네임 맹글링 (0) | 2024.04.14 |
---|---|
C++ for문 증감문에 전위연산자 vs 후위연산자 속도 차이 (1) | 2024.04.03 |
VTable 위치 파악 (0) | 2024.03.25 |
초기화자 리스트(intializer_list) (0) | 2024.03.08 |
유니폼 초기화 (0) | 2024.03.06 |