C++의 다 중 및 가상 함수 의 내부 구현 방법
다 태 성 은 간단하게'하나의 인터페이스,다양한 행위'로 요약 할 수 있다.
즉,서로 다른 대상 에 게 같은 메 시 지 를 보 내 면 서로 다른 대상 이 받 을 때 서로 다른 행동(즉 방법)을 하 는 것 이다.모든 대상 이 자신의 방식 으로 공 통 된 소식 에 호응 할 수 있다 는 것 이다.메시지 란 함 수 를 호출 하 는 것 이다.서로 다른 행 위 는 서로 다른 실현,즉 서로 다른 함 수 를 실행 하 는 것 을 말한다.이것 은 같은 코드 로 서로 다른 동작 을 실현 하 는 일반적인 기술 이다.이것 은 대상 을 대상 으로 프로 그래 밍 하 는 우월성 을 구현 하 였 다.
다 태 는 두 가지 로 나 뉜 다.
(1)컴 파일 시 다 중:함수 의 리 셋 과 템 플 릿 을 통 해 이 루어 집 니 다.
(2)실행 시 다 중:주로 가상 함 수 를 통 해 이 루어 집 니 다.
2.몇 가지 관련 개념
(1)덮어 쓰기,다시 쓰기(override)
override 는 기본 클래스 의 한 구성원 함 수 를 가상 함수 로 하고 파생 류 는 또 하나의 구성원 함 수 를 정의 하 며 함수 체 를 제외 한 나머지 부분 은 모두 기본 클래스 의 구성원 함수 와 같 습 니 다.함수 이름 만 같 고 형 삼 또는 반환 유형 이 다 르 면 override 가 아니 라 hide 라 고 할 수 없습니다.
(2)과부하(overload)
같은 작용 역 에서 태 어 난 여러 함수 의 이름 은 같 지만 형 삼 이 다른 함 수 를 가리킨다.컴 파일 러 는 컴 파일 할 때 실제 인삼 의 개수 와 유형 을 통 해 최종 호출 함 수 를 선택 합 니 다.
(3)숨 기기(숨 기기)
두 가지 로 나 뉜 다.
1)부분 변수 나 함수 가 전역 변수 나 함 수 를 숨 겼 습 니 다.
2)파생 류 는 기본 클래스 와 같은 이름 의 구성원 함수 나 구성원 변 수 를 가지 고 있다.
결과:전역 또는 기본 클래스 의 변수,함수 가 보이 지 않 습 니 다.
3.몇 가지 간단 한 예
/******************************************************************************************************
* File:PolymorphismTest
* Introduction: 。
* Author:CoderCong
* Date:20141114
* LastModifiedDate:20160113
*******************************************************************************************************/
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("1
");
}
virtual void fun()
{
printf("2
");
}
};
class B : public A
{
public:
void foo() // foo , ,
{
printf("3
");
}
void fun() //
{
printf("4
");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo(); // 1。
p->fun(); // 2。
p = &b;
p->foo(); // 1。 p ,p->foo 。
p->fun(); // 4。 。 p , 。p->fun 。 ,
return 0;
}
4.운행 시 다 형 및 가상 함수 의 내부 실현위의 몇 가지 간단 한 예 를 보고 나 는 문득 크게 깨 달 았 다.알 고 보 니 이것 이 다 형 이 었 다.이렇게 간단 하고 알 겠 다!
자,그럼 예 를 하나 더 봅 시다.
class A
{
public:
virtual void FunA()
{
cout << "FunA1" << endl;
};
virtual void FunAA()
{
cout << "FunA2" << endl;
}
};
class B
{
public:
virtual void FunB()
{
cout << "FunB" << endl;
}
};
class C :public A, public B
{
public:
virtual void FunA()
{
cout << "FunA1C" << endl;
};
};
int _tmain(int argc, _TCHAR* argv[])
{
C objC;
A *pA = &objC;
B *pB = &objC;
C *pC = &objC;
printf("%d %d
", &objC, objC);
printf("%d %d
", pA, *pA);
printf("%d %d
", pB, *pB);
printf("%d %d
", pC, *pC);
return 0;
}
실행 결과:5241376 1563032
5241376 1563032
5241380 1563256
5241376 1563032
세심 한 동 지 는 분명히 pB 에 문제 가 생 긴 것 을 발 견 했 을 것 입 니 다.왜 모두 obsc 를 가리 키 는 지침 이 고 pB 는 다른 사람의 값 과 다 릅 니까?
컴 파일 러 에 문제 가 생 긴 거 아니에요?
당연히 아니 지!우 리 는 먼저 결론 을 말한다.
(1)모든 가상 함 수 를 포함 하 는 클래스 는 가상 테이블(virtual table)을 생 성 합 니 다.이 표 는 대상 의 동적 유형 을 기록 하여 이 대상 의 가상 구성원 함 수 를 실행 할 때 진정 으로 실행 하 는 구성원 함 수 를 결정 합 니 다.
(2)여러 개의 기본 클래스 가 있 는 대상 에 대해 여러 개의 가상 표 가 있 고 모든 기본 클래스 는 하나의 가상 표 에 대응 하 는 동시에 가상 표 의 순서 와 계승 시의 순서 가 같다.
(3)모든 클래스 대상 이 사용 하 는 메모리 에서 가상 포인터 가 맨 앞 에 있 고 모든 가상 포인터 가 대응 하 는 가상 시 계 를 가리킨다.
먼저 간단 한 단일 기류 부터 말하자면:
class A
{
public:
virtual void FunA()
{
cout << "FunA1" << endl;
}
virtual void FunA2()
{
cout << "FunA2" << endl;
}
};
class C :public A
{
virtual void FunA()
{
cout << "FunA1C" << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A *pA = new A;
C *pC = new C;
typedef void (*Fun)(void);
Fun fun= (Fun)*((int*)(*(int*)pA));
fun();//pA
fun = (Fun)*((int*)(*(int*)pA) +1);
fun();//pA
fun = (Fun)*((int*)(*(int*)pC));
fun();//pC
fun = (Fun)*((int*)(*(int*)pC) + 1);
fun();//pC
return 0;
}
실행 결과:FunA1
FunA2
FunA1C
FunA2
좀 어 지 럽 죠?괜 찮 습 니 다.나 는 조금씩 설명 한다.pA 는 A 의 대상 에 대응 하고 우 리 는 이런 시 계 를 그 릴 수 있다.
이것 이 바로 대상*pA 의 가상 표 입 니 다.두 개의 가상 함 수 는 성명 순서 로 배열 되 어 있 습 니 다.pA 는 대상*pA 를 가리 키 며,*(int*)pA 는 이 가상 표를 가리 키 며,(Fun)*(int*)(*(int*)pA)는 FunA 를 가리 키 며,마찬가지 로(Fun)*(int*)(*(int*)pA)+1 은 FunA 2 를 가리킨다.그래서 앞의 두 가지 결과 가 나 왔 다.
뒤의 두 가지 결과 에 근거 하여 우 리 는*pC 의 허 표를 아래 그림 과 같이 추측 할 수 있다.
C 에 있 는 FunA 가 A 에 있 는 FunA 를 다시 쓰기(override)했 기 때문에 가상 테이블 에 있 는 가상 함수 의 주소 도 다시 썼 다 는 것 이다.
바로 이렇다.이것 이 바로 다 태 적 실현 의 내부 메커니즘 이다.
우 리 는 다시 최초의 문제 로 돌 아 왔 다.왜*pB 에 문제 가 생 겼 습 니까?
위의 결론 에 따 르 면 우 리 는 대담 하 게 추측 했다.C 는 A,B 에서 파생 된 것 이기 때문에 object 는 두 개의 가상 표 가 있 고 표 의 순서 로 인해 pA,pC 는 모두 A 에 대응 하 는 가상 표를 가리 키 며 pB 는 B 에 대응 하 는 가상 표를 가리킨다.실험 을 해서 우리 의 추측 이 정확 한 지 검증 하 자.
우 리 는 A,B,C 류 를 바 꾸 지 않 고 문제 중의 main 을 바 꿉 니 다.
int _tmain(int argc, _TCHAR* argv[])
{
C objC;
A *pA = &objA;
B *pB = &objC;
C *pC = &objC;
typedef void (*Fun)(void);
Fun fun = (Fun)*((int*)(*(int*)pC));
fun();//
fun = (Fun)*((int*)(*(int*)pC)+1);
fun();//
fun = (Fun)*((int*)(*((int*)pC+1)));
fun();<span style="white-space:pre"> </span>//
fun = (Fun)*((int*)(*(int*)pB));
fun();//pB
return 0;
}
하하,우리 의 추측 과 완전히 일치한다.FunA1C
FunA2
FunB
FunB
우 리 는 이러한 허 함수 도 를 그 릴 수 있다.
일단 이렇게 이해 하면 컴 파일 러 가 B*pB=&object 를 실행 할 때 할당 만 하 는 것 이 아니 라 해당 하 는 최적화 를 하여 pB 를 두 번 째 가상 표 로 가 리 켰 다.
이렇게 많은 말 을 했 습 니 다.저 는 허 함수 의 실현 원 리 를 간단하게 설 명 했 을 뿐 입 니 다.그런데 도대체 대상 내부 의 메모리 구 조 는 어떻게 됩 니까?클래스 데이터 구성원 과 여러 개의 가상 테이블 의 구체 적 인 메모리 레이아웃 은 어 떻 습 니까?컴 파 일 러 는 어떻게 값 을 부여 할 때 최 적 화 를 했 습 니까?나중에 얘 기 할 게 요.
이상 은 여러분 에 게 가 져 다 준 C++의 다 태 와 허 함수 의 내부 실현 방법의 모든 내용 입 니 다.많은 응원 부 탁 드 리 겠 습 니 다~
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Docker를 사용한 React 및 .NET Core 6.0 샘플 프로젝트 - 1부이 기사에서는 Entity Framework Core Code First 접근 방식을 사용하는 ASP.NET Core 6.0 WEP API의 CRUD(만들기, 읽기, 업데이트 및 삭제) 작업에 대해 설명합니다. 웹 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.