C++클래스 메모리 분포 분석

9969 단어 c + +메모리
일 을 잘 하려 면 먼저 그 기 구 를 이 롭 게 해 야 한다.우 리 는 먼저 Visual Studio 도 구 를 잘 사용 하고 다음 과 같이 한 걸음 한 걸음 해 야 한다.


왼쪽 에 있 는 C/C++->명령 행 을 선택 한 다음 다른 옵션 에/d1 reportAllClassLayout 를 쓰 면 모든 종류의 메모리 레이아웃 을 볼 수 있 습 니 다./d1 reportSingleClassLayout XXX(XXX 는 클래스 이름)를 쓰 면 지정 한 XXX 메모리 레이아웃 만 표시 합 니 다.최근 VS 버 전 은 이러한 설정 을 지원 합 니 다.
아래 와 같이 클래스 를 정의 할 수 있 습 니 다.

class Base
{
    int a;
    int b;
public:
    void CommonFunction();
};
그리고 컴 파일 하면 출력 상자 안에 이런 배열 이 있 는 것 을 볼 수 있 습 니 다.

메모리 정렬 요소 에 신경 쓰 고 싶 지 않 기 때문에 멤버 변 수 는 모두 int 형 으로 설정 합 니 다.
여기 서 일반 클래스 의 배열 방식 을 볼 수 있 습 니 다.구성원 변 수 는 성명 의 순서에 따라 배열(클래스 내 오프셋 이 0 으로 시작)하고 구성원 함수 가 메모리 공간 을 차지 하지 않 습 니 다.
계승 을 보고 다음 코드 를 뒤로 추가 합 니 다.

class DerivedClass: public Base
{
    int c;
public:
    void DerivedCommonFunction();
};
컴 파일 한 다음 에 다음 과 같은 메모리 분포(부모 류 의 메모리 분포 가 변 하지 않 습 니 다.여 기 는 하위 클래스 구성원 변수의 메모리 분포 만 토론 합 니 다)를 볼 수 있 습 니 다.

하위 클래스 가 부모 클래스 의 구성원 변 수 를 계승 하 는 것 을 볼 수 있 습 니 다.메모리 배열 에 있어 서 먼저 부모 클래스 의 구성원 변 수 를 배열 한 다음 에 하위 클래스 의 구성원 변 수 를 배열 합 니 다.마찬가지 로 구성원 함수 가 바이트 를 차지 하지 않 습 니 다.
기본 클래스 에 가상 함 수 를 추가 하고 Derived Class 를 잠시 설명 합 니 다.이 메모리 배열 을 보십시오.

class Base
{
    int a;
    int b;
public:
    void CommonFunction();
    void virtual VirtualFunction();
};

이 메모리 구조 도 는 두 부분 으로 나 뉘 었 는데,위 에는 메모리 분포 이 고,아래 는 가상 표 이 니,우 리 는 하나씩 보 자.VS 가 져 온 컴 파일 러 는 메모리 의 시작 부분(0 주소 오프셋)에 가상 시계 바늘 을 두 고 구성원 변수 입 니 다.아래 에 가상 표 가 생 성 되 었 습 니 다.&Base 1 에 바짝 붙 어 있 습 니 다.meta 뒤의 0 은 이 가상 표 에 대응 하 는 가상 포인터 가 메모리 에 분포 되 어 있 음 을 나타 낸다.아래 에 가상 함수 가 열거 되 어 있 고 왼쪽 에 있 는 0 은 이 가상 함수 의 번호 이 며 여 기 는 하나의 가상 함수 만 있 기 때문에 한 가지 만 있 을 뿐이다.만약 에 여러 개의 가상 함수 가 있 으 면 번호 가 1 이 고 2 인 가상 함수 가 열 려 있 을 것 이다.
컴 파일 러 는 구조 함수 에서 이 가상 포인터 와 가상 표를 만 드 는 것 이다.
그렇다면 컴 파일 러 는 어떻게 가상 포인터 와 가상 표를 이용 하여 다 형 을 실현 합 니까?이 렇 습 니 다.가상 함 수 를 포함 하 는 부모 클래스 의 대상 을 만 들 때 컴 파일 러 는 대상 구조 에서 가상 시계 바늘 을 부모 클래스 의 가상 함 수 를 가리 킵 니 다.마찬가지 로 하위 클래스 의 대상 을 만 들 때 컴 파일 러 는 구조 함수 에서 가상 시계 포인터(하위 클래스 는 하나의 가상 시계 포인터 만 있 고 부모 클래스 에서 왔 습 니 다)를 하위 클래스 의 가상 표(이 가상 표 안의 가상 함수 입구 주 소 는 하위 클래스 입 니 다)를 가 리 킵 니 다.
따라서 Base*p=new Derived()를 호출 하 는 경우;생 성 된 것 은 하위 클래스 의 대상 입 니 다.구조 할 때 하위 클래스 의 가상 포인터 가 하위 클래스 의 가상 시 계 를 가리 키 고 이 어 Derived*에서 Base*로 전환 하 는 것 은 가상 시계 지침 을 바 꾸 지 않 았 습 니 다.그래서 이때 p->VirtualFunction 은 실제 p->vfptr->VirtualFunction 입 니 다.이 는 구조 할 때 하위 클래스 의 VirtualFunction 을 가리 키 기 때문에 하위 클래스 의 가상 함 수 를 호출 합 니 다.이것 이 다 형 이다.
아래 에 하위 클래스 를 추가 하고 하위 클래스 에 가상 함 수 를 추가 합 니 다.아래 와 같이:

class DerivedClass: public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};
하위 메모리 의 배열 은 다음 과 같 습 니 다.

전반 부 는 메모리 분포 로 볼 수 있 습 니 다.가상 시계 바늘 이 계승 되 었 고 메모리 배열 의 시작 부분 에 있 습 니 다.다음은 부모 류 의 구성원 변수 a 와 b 입 니 다.마지막 으로 하위 클래스 의 구성원 변수 c 입 니 다.가상 시계 바늘 은 하나 밖 에 없 으 며 하위 클래스 는 가상 시계 바늘 을 만 들 지 않 았 습 니 다.하반부 의 허표 상황 은 부류 와 같다.
우 리 는 하위 클래스 를 코드 로 바 꾸 었 습 니 다.이렇게:

class DerivedClass1 : public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction2();
};
이때 우 리 는 부모 류 의 가상 방법 을 덮어 쓰 지 않 고 새로운 하위 클래스 의 가상 방법 을 다시 밝 혔 음 을 알 게 되 었 습 니 다.메모리 분 포 는 다음 과 같 습 니 다.

아니면 하나의 가상 시계 포인터 만 있 지만 아래 의 가상 표 의 내용 이 바 뀌 었 습 니 다.가상 표 의 0 번 은 부모 류 의 Virtual Function 이 고 1 번 은 하위 류 의 Virtual Function 2 입 니 다.즉,DerivedClass 의 대상 을 정의 하면 구조 시 가상 시계 포인터 가 이 가상 시 계 를 가리 키 고,이후 에 가상 함수 가 호출 되면 부모 클래스 에서 해당 하 는 가상 함 수 를 찾 고,가상 함수 가 호출 되면 하위 클래스 에서 해당 하 는 가상 함 수 를 찾 게 된다.
우 리 는 다시 한 번 종 류 를 개조 했다.이렇게.

class DerivedClass1 : public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
    void virtual VirtualFunction2();
};
우 리 는 부모 클래스 의 가상 함수 도 덮어 쓰 고 새로 추 가 된 가상 함수 도 있 습 니 다.그러면 다음 과 같은 메모리 분포 라 고 예상 할 수 있 습 니 다.

다음은 다 중 계승 을 논의 하 겠 습 니 다.코드 는 다음 과 같 습 니 다.

class Base
{
    int a;
    int b;
public:
    void CommonFunction();
    void virtual VirtualFunction();
};


class DerivedClass1: public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedClass2 : public Base
{
    int d;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void DerivedDerivedCommonFunction();
    void virtual VirtualFunction();
};
메모리 분 포 는 부모 클래스 에서 하위 클래스 까지 다음 과 같 습 니 다.

Base 에 가상 시계 포인터 가 있 습 니 다.주소 가 0 으로 이동 합 니 다.

Derived Class 1 은 Base 를 계승 하 였 으 며,메모리 배열 은 선친 류 의 하위 클래스 입 니 다.

Derived Class 2 의 경우 Derived Class 1 과 유사 하 다.

다음은 이 유형의 Derived Derived Class 를 살 펴 보 겠 습 니 다.외 향 적 으로 볼 때 계승 해 온 두 개의 부모 클래스 인 Derived Class 1 과 Derived Class 2,그리고 자신의 구성원 변수 e 가 병렬 적 으로 배열 되 어 있 습 니 다.Derived Class 1 은 구성원 변수 c 와 Base,Base 에 0 주소 오프셋 의 가상 시계 바늘 을 포함 하고 그 다음 에 구성원 변수 a 와 b 를 포함 합 니 다.Derived Class 2 의 메모리 배열 은 Derived Class 1 과 유사 하 며,Derived Class 2 에 도 Base 가 있다 는 것 을 알 게 되 었 습 니 다.

여기 두 개의 가상 표 가 있 습 니 다.각각 Derived Class 1 과 Derived Class 2 를 대상 으로&Derived Dericed Classmeta 아래 의 숫자 는 첫 번 째 주소 오프셋 입 니 다.아래 의 가상 표 에 있 는-16 은 이 가상 표를 가리 키 는 가상 포인터 의 메모리 오프셋 을 표시 합 니 다.이것 은 바로 Derived Class 2 의{vfptr}이 Derived Derived Class 의 메모리 오프셋 입 니 다.
만약 에 가상 계승 을 채택 한다 면 아래 와 같이:

class DerivedClass1: virtual public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedClass2 : virtual public Base
{
    int d;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedDerivedClass :  public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void DerivedDerivedCommonFunction();
    void virtual VirtualFunction();
};

Base 클래스 는 변 하지 않 았 지만 아래 를 보 세 요:

Derived Class 1 은 이미 변화 가 있 습 니 다.원래 먼저 가상 시계 포인터 와 Base 구성원 변 수 를 배열 하고 vfptr 는 0 주소 오프셋 에 있 습 니 다.하지만 지금 은 두 개의 가상 시계 바늘 이 있 습 니 다.하 나 는 vbptr 이 고 다른 하 나 는 vfptr 입 니 다.vbptr 는 이 DerivedClass 1 에 대응 하 는 가상 시계 포인터 입 니 다.DerivedClass 1 의 가상 시계 vbtable 을 가리 키 고 다른 vfptr 는 가상 기본 표 에 대응 하 는 가상 포인터 입 니 다.vftable 을 가리 키 고 있 습 니 다.
다음은 두 장의 가상 표 입 니 다.첫 번 째 표 는 vbptr 가 가리 키 는 표 입 니 다.8 은{vbptr}과{vfptr}의 오프셋 을 표시 합 니 다.두 번 째 시 계 는 vfptr 가 가리 키 는 시계 입 니 다.-8 은 이 시계 에 대응 하 는 가상 포인터 가 메모리 의 오프셋 에 있 음 을 가 리 킵 니 다.


Derived Class 2 의 메모리 분 포 는 Derived Class 1 과 유사 하 며,마찬가지 로 두 개의 가상 포인터 가 각각 두 장의 가상 시계(두 번 째 는 가상 기류 표)를 가리킨다.

다음은 Derived Derived Class 의 메모리 분 포 를 자세히 살 펴 보 겠 습 니 다.이 안 에는 세 개의 가상 포인터 가 있 지만 base 는 하나 밖 에 없습니다.첫 번 째 가상 시 계 는 Derived Class 1 이 들 어 있 습 니 다.20 은 가상 포인터{vbptr}에서 가상 포인터{vfptr}의 거 리 를 표시 합 니 다.두 번 째 가상 시 계 는 Derived Class 2 가 들 어 있 습 니 다.12 는 가상 포인터{vbptr}에서 가상 포인터{vfptr}의 거 리 를 표시 합 니 다.마지막 시 계 는 가상 시계 입 니 다.-20 은 가상 포인터{vfptr}가 메모리 에서 의 오프셋 을 가 리 킵 니 다.


가상 상속 의 역할 은 기본 클래스 에 대한 중복 을 줄 이 고 대 가 는 가상 포인터 의 부담(더 많은 가상 포인터)을 증가 시 키 는 것 이다.
다음은 요약 합 니 다(기본 클래스 에 가상 함수 가 있 을 때).
1.모든 종 류 는 가상 포인터 와 가상 시계 가 있다.
2.가상 계승 이 아니라면 자 류 는 아버지 류 의 가상 지침 을 계승 하고 자신의 가상 시 계 를 가리킨다(대상 구조 에서 발생 할 때).몇 개의 가상 함수 가 있 으 면 가상 표 안의 항목 이 얼마나 있 을 까?다 중 계승 시 여러 개의 기본 클래스 허 표 와 허 지침 이 존재 할 수 있 습 니 다.
3.가상 계승 이 라면 자 류 는 두 개의 가상 지침 이 있 고 하 나 는 자신 을 가리 키 는 가상 시계 이 며 다른 하 나 는 가상 시계 이 며 다 중 계승 시 가상 시계 와 가상 시계 지침 이 있 고 한 개 만 있 습 니 다.
이상 은 C++클래스 메모리 분 포 를 분석 하 는 상세 한 내용 입 니 다.C++클래스 메모리 분포 에 관 한 자 료 는 다른 관련 글 을 주목 하 십시오!

좋은 웹페이지 즐겨찾기