C++허 함수 의 작업 원 리 를 상세히 해석 하 다.

8884 단어 c + +가상 함수
정적 바 인 딩 과 동적 바 인 딩
정적 바 인 딩 과 동적 바 인 딩 에 대해 토론 합 니 다.먼저 바 인 딩 을 이해 해 야 합 니 다.바 인 딩 이 무엇 입 니까?함수 호출 은 함수 자체 와 관련 되 고 구성원 접근 과 변수 메모리 주소 간 의 관 계 를 바 인 딩 이 라 고 합 니 다.바 인 딩 을 이해 한 후에 정적 과 동 태 를 이해 합 니 다.
4.567917.정적 바 인 딩:프로그램 컴 파일 과정 에서 함수 호출 과 응답 호출 에 필요 한 코드 를 결합 시 키 는 과정 을 정적 바 인 딩 이 라 고 합 니 다.컴 파일 기간 에 발생 합 니 다4.567917.동적 바 인 딩:실행 기간 에 인용 대상 의 실제 유형 을 판단 하고 실제 유형 에 따라 해당 하 는 방법 을 호출 하 는 것 을 말한다.프로그램 이 실행 되 는 과정 에서 함수 호출 과 호출 에 필요 한 코드 를 결합 시 키 는 과정 을 동적 바 인 딩 이 라 고 합 니 다.운행 기간 에 발생 하 다.
C++에서 동적 바 인 딩
C++에서 동적 바 인 딩 은 가상 함 수 를 통 해 이 루어 지 며 다 중 실현 의 구체 적 인 형식 입 니 다.허 함 수 는 허 함수 표를 통 해 이 루어 진다.이 표 에는 가상 함수 의 주 소 를 기록 하여 계승,덮어 쓰 는 문 제 를 해결 하고 동적 연결 시 대상 의 실제 유형 에 따라 정확 한 함 수 를 호출 할 수 있 도록 합 니 다.이 가상 함수 표 는 어디 에 있 습 니까?C++표준 규격 설명서 에 따 르 면 컴 파일 러 는 가상 함수 표 의 지침 이 대상 인 스 턴 스 의 맨 앞 에 존재 하 는 위 치 를 확보 해 야 한다(이것 은 가상 함수 의 오프셋 을 정확하게 얻 기 위해 서 이다).즉,우 리 는 대상 인 스 턴 스 의 주 소 를 통 해 이 가상 함수 표를 얻 은 다음 에 그 중의 함수 지침 을 옮 겨 다 니 며 해당 하 는 함 수 를 호출 할 수 있다.
가상 함수 의 작업 원리
동적 바 인 딩 을 이해 하려 면 허 함수 의 작업 원 리 를 알 아야 한다.C++에서 허 함수 의 실현 은 일반적으로 허 함수 표를 통 해 이 루어 진다(C++규범 에 구체 적 으로 어떤 방법 을 사용 하 는 지 규정 되 지 않 았 지만 대부분의 컴 파일 러 업 체 들 은 이 방법 을 선택한다).클래스 의 가상 편지 수 표 는 연속 적 인 메모리 로 메모리 단위 마다 JMP 명령 의 주 소 를 기록 합 니 다.컴 파일 러 는 가상 함수 가 있 는 모든 클래스 에 가상 함수 표를 만 들 고 이 가상 함수 표 는 이러한 모든 대상 에 의 해 공 유 됩 니 다.클래스 의 모든 가상 구성원 은 가상 편지 의 한 줄 을 차지한다.클래스 에 N 개의 허 함수 가 있 으 면 허 함수 표 는 N*4 바이트 의 크기 가 있 습 니 다.
가상 함수(virtual)는 가상 함수 표를 통 해 이 루어 집 니 다.이 표 에 서 는 주로 하나의 가상 함수 의 주소 표 입 니 다.이 표 는 계승,덮어 쓰 는 문 제 를 해결 하여 실제 함 수 를 진실 하 게 반영 하도록 합 니 다.이렇게 하면 가상 함수 가 있 는 클래스 의 인 스 턴 스 에서 이 시 계 를 가리 키 는 포인터 의 메모리(대상 인 스 턴 스 의 맨 앞 에 있 음)를 분 배 했 기 때문에 부모 클래스 의 지침 으로 하위 클래스 를 조작 할 때 이 가상 함수 표 는 특히 중요 하고 실제 호출 해 야 할 함 수 를 가리킨다.그것 은 어떻게 가 리 켰 습 니까?나중에 얘 기 할 게 요.
JMP 명령 은 어 셈 블 리 언어 에서 무조건 이동 명령 으로 무조건 이동 명령 은 메모리 의 모든 프로그램 으로 이동 할 수 있 습 니 다.이전 주 소 는 명령 에서 제시 할 수도 있 고 레지스터 에서 제시 하거나 레지스터 에서 지적 할 수도 있다.
우선 우 리 는 가상 함 수 를 가 진 기본 클래스 를 정의 합 니 다.

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!
"; } virtual void fun2(){ cout<<"base fun2!
"; } virtual void fun3(){ cout<<"base fun3!
"; } int a; };

Base 류 의 메모리 레이아웃 에 첫 번 째 위치 에 가상 함수 표 지침 을 저장 하고 그 다음 에 Base 의 구성원 변 수 를 볼 수 있 습 니 다.또한,가상 함수 표 가 존재 합 니 다.이 표 에는 Base 류 의 모든 virtual 함수 가 저장 되 어 있 습 니 다.
가상 함수 표 포인터 가 대상 인 스 턴 스 의 맨 앞 에 놓 여 있 는 이상 우 리 는 코드 를 통 해 가상 함수 표를 방문 할 수 있 습 니 다.아래 코드 를 통 해 가상 함수 표 에 대한 이 해 를 강화 할 수 있 습 니 다.

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!
"; } virtual void fun2(){ cout<<"base fun2!
"; } virtual void fun3(){ cout<<"base fun3!
"; } int a; }; int _tmain(int argc, _TCHAR* argv[]) { typedef void(*pFunc)(void); Base b; cout<<" :"<<(int*)(&b)<<endl; // , pFunc pfun; pfun=(pFunc)*((int*)(*(int*)(&b))); // , pfun(); pfun=(pFunc)*((int*)(*(int*)(&b))+1); pfun(); pfun=(pFunc)*((int*)(*(int*)(&b))+2); pfun(); system("pause"); return 0; }
실행 결과:

이 예 를 통 해 허 함수 표 지침,허 함 수 표 등 을 충분히 이해 할 수 있 습 니 다.다음은 좀 더 깊이 들 어가 자.C++는 어떻게 기본 포인터 와 가상 함 수 를 이용 하여 다 형 을 실현 합 니까?여기 서 우 리 는 계승 환경 에서 허 함수 표 가 어떻게 일 하 는 지 알 아야 한다.현 재 는 단 상속 만 이해 하고 허 상속 에 대해 서 는 다 중 상속 은 나중에 이해 하 겠 다.
단일 계승 코드 는 다음 과 같 습 니 다.

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!
"; } virtual void fun2(){ cout<<"base fun2!
"; } virtual void fun3(){ cout<<"base fun3!
"; } int a; }; class Child:public Base { public: void fun1(){ cout<<"Child fun1
"; } void fun2(){ cout<<"Child fun2
"; } virtual void fun4(){ cout<<"Child fun4
"; } };
메모리 레이아웃 대비:


비 교 를 통 해 우 리 는 볼 수 있다.
4.567917.단일 계승 에서 Child 류 는 Base 류 의 동명 허 함 수 를 덮 었 고 허 함 수 표 에서 대응 하 는 위치 가 Child 류 의 새로운 함수 로 바 뀌 었 으 며 겹 치지 않 은 함 수 는 변화 가 없 었 다4.567917.하위 클래스 자신의 허 함수 에 대해 허 함 수 표 뒤에 직접 추가 합 니 다.
또한,우 리 는 클래스 Child 와 클래스 Base 에는 모두 vfptr 포인터 가 하나 밖 에 없다 는 것 을 알 게 되 었 습 니 다.앞에서 말 했 듯 이 이 지침 은 가상 함수 표를 가리 키 고 있 습 니 다.우 리 는 각각 child 와 클래스 Base 의 vfptr 를 출력 합 니 다.

int _tmain(int argc, _TCHAR* argv[])
{
	typedef void(*pFunc)(void);
	Base b;
	Child c;
	cout<<"Base          :"<<(int*)(&b)<<endl;
	cout<<"Child          :"<<(int*)(&c)<<endl;

	system("pause");
	return 0;
}
실행 결과:

클래스 Child 와 클래스 Base 는 각각 자신의 가상 함수 표 포인터 vfptr 와 가상 함수 표 vftable 을 가지 고 있 음 을 알 수 있 습 니 다.
다음 코드 는 부모 클래스 와 기본 클래스 가 서로 다른 가상 함수 표를 가지 고 있 음 을 설명 합 니 다.같은 클래스 는 같은 가상 함수 표를 가지 고 있 습 니 다.같은 클래스 의 서로 다른 대상 의 주소(가상 함수 표 지침 을 저장 하 는 주소)가 다 릅 니 다.

int _tmain(int argc, _TCHAR* argv[])
{
	Base b;
	Child c1,c2;
	cout<<"Base         :"<<(int*)(*(int*)(&b))<<endl;
	cout<<"Child c1        :"<<(int*)(*(int*)(&c1))<<endl;	//            
	cout<<"Child c2        :"<<(int*)(*(int*)(&c2))<<endl;

	system("pause");
	return 0;
}

이 파생 클래스 의 대상 을 정의 할 때 먼저 기본 클래스 의 구조 함 수 를 호출 한 다음 에 vfptr 를 초기 화하 고 마지막 으로 파생 클래스 의 구조 함수(이 진 시야 에서 볼 때 기본 클래스 는 큰 구조 체 이 며,그 중에서 this 포인터 가 시작 하 는 네 개의 바이트 에 가상 함수 헤더 지침 을 저장 합 니 다.하위 클래스 의 구조 함 수 를 실행 할 때 먼저 기본 클래스 구조 함 수 를 호출 합 니 다.this 지침 은 매개 변수 로 기본 클래스 구조 함수 에 기본 클래스 의 vfptr 를 입력 한 다음 에 하위 클래스 의 구조 함수 로 돌아 가 하위 클래스 의 vfptr 를 입력 하고 기본 클래스 가 입력 한 vfptr 를 덮어 씁 니 다.이렇게 해서 vfptr 초기 화 완료).즉,vfptr 는 vftable 이 구조 함수 기간 에 발생 한 것 을 가리킨다.
동적 바 인 딩 예:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!
"; } virtual void fun2(){ cout<<"base fun2!
"; } virtual void fun3(){ cout<<"base fun3!
"; } int a; }; class Child:public Base { public: void fun1(){ cout<<"Child fun1
"; } void fun2(){ cout<<"Child fun2
"; } virtual void fun4(){ cout<<"Child fun4
"; } }; int _tmain(int argc, _TCHAR* argv[]) { Base* p=new Child; p->fun1(); p->fun2(); p->fun3(); system("pause"); return 0; }
실행 결과:

위의 메모리 레이아웃 과 결합:

사실은 new Child 에서 하위 클래스 의 대상 을 구 축 했 습 니 다.하위 클래스 의 대상 은 위 에서 말 한 바 와 같이 구조 함수 기간 에 가상 함수 표 포인터 vfptr 가 Child 류 를 가리 키 는 가상 함수 표를 완 성 했 습 니 다.이 대상 의 주 소 를 Base 형식의 포인터 p 에 할당 하 였 습 니 다.p->fun 1()을 호출 할 때 가상 함수 임 을 발 견 했 습 니 다.가상 함수 포인터 로 가상 함수 표 에 대응 하 는 가상 함수 의 주 소 를 찾 았 습 니 다.여기 가 바로&Child::fun 1 입 니 다.호출 p->fun 2()상황 이 같 습 니 다.p->fun 3()을 호출 할 때 하위 클래스 는 부모 클래스 의 허 함 수 를 다시 쓰 지 않 았 으 나 허 함수 포인터 로 허 함수 표를 찾 았 습 니 다.해당 함수 주 소 는&Base:fun 3 입 니 다.그래서 위의 운행 결 과 는 위의 그림 과 같다.
여기까지,당신 은 왜 하위 클래스 인 스 턴 스 를 가리 키 는 기본 포인터 가 하위 클래스(가상)함 수 를 호출 할 수 있 는 지 이미 알 고 있 습 니까?모든 인 스 턴 스 대상 에 vfptr 포인터 가 존재 합 니 다.컴 파일 러 는 vfptr 의 값 을 먼저 꺼 냅 니 다.이 값 은 가상 함수 표 vftable 의 주소 이 고 이 값 에 따라 vftable 에서 목표 함 수 를 호출 합 니 다.따라서 vfptr 가 다 르 면 가리 키 는 가상 함수 표 vftable 이 다 르 고 서로 다른 가상 함수 표 에 대응 하 는 가상 함수 주 소 를 저장 하여 다 중'효과'를 실현 합 니 다.
이상 은 C++허 함수 의 작업 원 리 를 상세 하 게 설명 하 는 상세 한 내용 입 니 다.더 많은 C+허 함수 에 관 한 자 료 는 우리 의 다른 관련 글 에 관심 을 가 져 주 십시오!

좋은 웹페이지 즐겨찾기