C++의 다 중 상속 실현 과 자바 의 차이
필 자 는 학교 모집 면접 에서 유명한 질문 인'C++자바 와 어떻게 다 형 을 실현 하 느 냐'는 질문 을 받 고 불행 하 게 도 전복 되 었 다.너무 유명 해서 오히려 준 비 를 하지 않 고 허 함 수표 와 관련 이 있다 는 것 만 알 고 있다.면접 후 C++와 자바 다 중 실현 의 공통점 과 차이 점 을 비교 해 기록 했다.
C++다 중 가상 포인터 구현
먼저 C++.다 중,즉 하위 클래스 가 부모 클래스 구성원 함수 에 대해 재 작성(Override)한 후,하위 클래스 지침 을 부모 클래스 에 할당 한 다음,이 부모 클래스 지침 에 구성원 함 수 를 호출 하고,하위 클래스 재 작성 버 전의 구성원 함 수 를 호출 합 니 다.간단 한 예:
class Parent1 {
public:
virtual void sayHello() { printf("Hello from parent1!
"); }
};
class Child : public Parent1 {
public:
virtual void sayHello() { printf("Hello from child!
"); }
};
int main() {
Parent1 *p = new Child();
p->sayHello(); // get "Hello from child!"
}
먼저,바 텀 실현 에 있어 구성원 함 수 는 첫 번 째 매개 변 수 를 대상 지침 으로 하 는 함수 임 을 알 아야 합 니 다.컴 파일 러 는 자동 으로 대상 지침 을 함수 파라미터 에 추가 하고 this 지침 이 라 고 명명 합 니 다.그 밖 에 일반 함수 와 본질 이 다 르 지 않 습 니 다.비 다 중 구성원 함수 호출 은 비 구성원 함수 호출 과정 과 기본적으로 일치 하 며,매개 변수 목록(매개 변수 목록 에 대상 포인터 형식 포함)과 함수 명 에 따라 컴 파일 할 때 실제 호출 함 수 를 확정 합 니 다.다 형 을 실현 하기 위해 서 는 대상 포인터 유형 에 따라 함수 서명 을 추정 할 수 없습니다.즉,예 에서
p->sayHello()
이 줄 코드 는 실행 할 때 p 의 유형 에 따라 호출 된 함수 가Parent::sayHello
인지 Child:sayHello
인지 확인 할 수 없습니다.다 중 메커니즘 에서 모든 종류의 부모 클래스 와 하위 클래스 는 데이터 구조 에서 하나의 지침 을 더 가 져 가 야 한다.이 지침 은 이러한 종류의 가상 함수 표를 가리킨다.클래스 의 가상 함수 표 는 다시 쓸 수 있 는 모든 함수 포인터 표 입 니 다.대상 이 만 들 때 실제 유형 에 따라 가상 함수 포인터 가 가리 키 는 가상 함수 목록 을 결정 합 니 다.예 를 들 어 위의 예 에서 Parent 1 과 Child 류 의 가상 함수 목록 은 모두 하나의 함수 만 있 는데 각각
Parent1::sayHello
과 Child::sayHello
이다.컴 파일 러 는 컴 파일 할 때 함 수 를'가상 함수 표 의 N 번 째 함수 참조'라 는 명령 으로 호출 할 것 이다.예 를 들 어 본 사례 에서'가상 함수 표 의 첫 번 째 함수 참조'로 번역 할 것 이다.실행 할 때 가상 함 수 표 의 진정한 함수 지침 을 읽 습 니 다.실행 시 CPU 대 가 는 기본적으로 포인터 참조 와 다음 표 접근 입 니 다.Parent 1 과 Child 대상 모두 사용자 정의 데이터 구조 가 없습니다.다음 코드 를 실행 하면 Parent 1 과 Child 대상 의 실제 데이터 구조 크기 가 모두 8 바이트 인 것 을 확인 할 수 있 습 니 다.즉,가상 함수 목록 지침 만 있 습 니 다.Parent 1 과 Child 1 대상 을 64 비트 정수 로 출력 하면 p1,p2 의 값 이 같 고 p3 는 앞의 두 가지 와 다르다.이 값 도 상응하는 종류의 허함 수표 주소 이다.
Parent1* p1 = new Parent1();
Parent1* p2 = new Parent1();
Parent1* p3 = new Child();
printf("sizeof Parent1: %d, sizeof Child: %d
",
sizeof(Parent1), sizeof(Child));
printf("val on p1: %lld
", *(int64_t*)p1);
printf("val on p2: %lld
", *(int64_t*)p2);
printf("val on p3: %lld
", *(int64_t*)p3);
C++다 중 상속매우 재 미 있 는 문제 가 있 습 니 다.C++다 중 상속 이 발생 할 때 다 중 태 를 어떻게 지원 하 는 지.방금 언급 한 다 중 원 리 는 컴 파일 러 가 구성원 함 수 를'가상 함수 표 의 N 번 째 함수 참조'로 컴 파일 하 는 것 이다.가상 함수 표 는 대상 데이터 구조 에서 의 위치 와 가상 함수 목록 에서 의 몇 번 째 함 수 를 컴 파일 할 때 모두 확인 해 야 한다.다 중 계승 대상 이 하나의 가상 함수 목록 만 있다 면 부모 류 의 가상 함수 목록 의 위치 가 충돌 할 것 입 니 다.만약 여러 개의 가상 함수 목록 이 있다 면,컴 파일 할 때 가상 함수 목록 포인터 가 데이터 구조 에 있 는 위 치 를 확정 하기 어렵다.C++는 매우 정교 한 방법 을 취 했다.모든 부모 류 의 데이터 구조(가상 포인터 목록 포함)를 이 대상 의 데이터 구조 에 순서대로 배열 하고 이 대상 의 지침 은 데이터 구조의 시작 위 치 를 정상적으로 가리킨다.포인터 가 유형 변환 이 발생 할 때 C+컴 파 일 러 는 포인터 의 값 을 가능 한 한 조정 하여 이 포인터 유형 이 대응 해 야 할 위 치 를 가리 키 도록 합 니 다.지침 의 값 은 이 과정 에서 변화 가 생 겼 다.
예 를 들 어 Child 류 는 Parent 1,Parent 2 두 종 류 를 계승 하고 Child 포인터 가 Parent 1 포인터 로 전 환 될 때 포인터 의 값 을 조정 하지 않 습 니 다.Parent 1 은 Child 의 첫 번 째 부류 이기 때 문 입 니 다.그러나 Child 를 Parent 2 로 변환 할 때 는 Parent 1 데이터 구조 길이 의 값 을 추가 해 해당 Parent 2 데이터 구조 시작 위 치 를 가리 키 도록 지침 을 지정 해 야 한다.이 예 에서 Parent 1 데이터 구 조 는 가상 함수 목록 포인터 만 있 고 64 비트 기기 의 길 이 는 8 입 니 다.따라서 Child 포인터 가 Parent 2 포인터 로 바 뀌 었 을 때 그 값 이 8 증가 하 였 습 니 다.
class Parent1 {
public:
virtual void sayHello() { printf("Hello from parent1!
"); }
};
class Parent2 {
public:
virtual void sayHi() { printf("Hi from Parent2!
"); }
};
class Child : public Parent1, public Parent2 {
public:
virtual void sayHello() { printf("Hello from child!
"); }
virtual void sayHi() { printf("Hi from child!
"); }
};
int main() {
Child *p = new Child();
printf("size of Child: %d", sizeof(Child));
printf("pointer val as Child*: %lld
", int64_t(p));
printf("pointer val as Parent1*: %lld
", int64_t((Parent1*)p));
printf("pointer val as Parent2*: %lld
", int64_t((Parent2*)p));
}
이 코드 를 실행 하면 Child 데이터 구조의 크기 가 16,즉 두 개의 지침 으로 늘 어 난 것 을 발견 할 수 있 습 니 다.또한 포인터 의 값 은 두 번 의 유형 이 바 뀔 때 다 르 고 64 비트 기기 에서 8 개의 바이트,즉 Parent 1 의 데이터 구조 크기 가 다르다.또한 p 를 Void 포인터 로 변환 하고 Parent 포인터 로 변환 하면 컴 파 일 러 가 이 오프셋 을 정확하게 추정 하지 못 하고 정의 되 지 않 은 행동 이 발생 합 니 다.이 특성 은 사실 매우 재 미 있 는 사실 을 설명 한다.C+컴 파일 러 는 컴 파일 할 때 포인터 의 오프셋 을 추정 할 수 있다.그러면 컴 파일 러 도 이 포인터 가 대상 을 가리 키 는 실제 유형 을 추정 할 수 있 을 것 이다.그렇다면 컴 파일 할 때 대상 의 실제 유형 을 추정 할 수 있 는 이상 가상 함수 표를 사용 하면 무슨 소 용이 있 습 니까?정확 한 함수 호출 을 직접 추정 하면 되 지 않 습 니까?문 제 는 컴 파일 할 때 다 중 함수 호출 을 추정 하면 서로 다른 유형의 대상 에 게 서로 다른 바 이 너 리 코드 를 생 성 하 는 것 을 의미한다.같은 줄 코드 는 포인터 값 에 따라 함수 호출 이 다 릅 니 다.이렇게 되면 제3자 라 이브 러 리 가 소스 코드 를 제공 하여 관련 추정 을 해 야 한 다 는 것 을 의미한다.템 플 릿 라 이브 러 리 와 유사 하 다.이것 은 모두 받 아들 일 수 없 는 것 이기 때문에 가상 함수 목록 은 여전히 필요 하 다.가상 함수 목록 을 통 해 포인터 코드 를 사용 하면 일치 하 는 기계 코드 를 만 들 수 있 습 니 다.
다른 측면 에서 볼 때 컴 파일 러 는 완전한 App 을 컴 파일 할 때 모든 변수의 실제 유형 을 추정 할 수 있 지만 너무 많은 문맥 과 연결 해 야 합 니 다.코드 를 컴 파일 하려 면 이 코드 가 파 라 메 터 를 입력 해 야 합 니 다.형식 을 제외 한 컨 텍스트 정 보 를 입력 하고 컨 텍스트 정보 에 따라 바 이 너 리 파일 을 생 성 해 야 합 니 다.이것 은 받 아들 일 수 없습니다.
자바 다 중 비교
자바 의 다 중 체 제 는 C+보다 간단 하기 때문에 이론 적 으로 C+의 체 제 를 사용 하여 자바 다 형 을 실현 할 수 있다.그러나 C++는 자바 와 결정적 인 차이 가 있 습 니 다.C++는 부모 구성원 에 게 가상 키 워드 를 수정 해 야 다시 쓸 수 있 습 니 다.이 는 컴 파일 러 가 부모 클래스 를 컴 파일 할 때 그 함수 들 이 재 작성 될 수 있 음 을 확인 할 수 있 기 때문에 재 작성 이 불가능 한 함수 에 대해 직접 컴 파일 할 때 호출 할 구체 적 인 함 수 를 결정 하고 재 작성 가능 한 함수 에 대해 서 는 가상 포인터 로 처리 할 수 있다 는 것 을 의미한다.자바 의 방법 은 기본적으로 다시 쓸 수 있 기 때문에 자바 방법 호출 은 모두 가상 함수 목록 을 조회 하 는 과정 을 거 쳐 C++다시 쓰 지 않 는 함수 보다 비용 이 많이 든다 고 볼 수 있 습 니 다.
자바 는 다 중 계승 을 지원 하지 않 지만 자바 는 인터페이스 인 터 페 이 스 를 지원 합 니 다.인 터 페 이 스 는 다 중 계승 과 비슷 한 점 이 있 습 니 다.가상 함수 표를 간단하게 사용 하여 찾 을 수 없습니다.클래스 는 실 현 된 모든 인터페이스 에 가상 함수 목록 을 만들어 야 합 니 다.C++와 유사 합 니 다.OpenJDK 문서 지적클래스 정의 에서 인터페이스 의 가상 함수 목록 을 찾 는 방법 은 매우 거 칠 습 니 다.클래스 가 실 현 된 모든 인터페이스 목록 에서 찾 습 니 다.문서 에서 진정한 다 중 상속 은 보기 드 문 것 으로 통상 적 으로 단일 상속 으로 귀 결 될 수 있다 고 지적 했다.이 과정 에 대해 여러 가지 최적화 가 있 을 수 있 으 므 로 필 자 는 깊이 이해 하지 못 했다.
자바 와 C++의 차이 점 을 생각 합 니 다.C++가 실행 되 지 않 았 을 때 유형 은 컴 파일 러 가 컴 파일 할 때 포인터 가 가리 키 는 위치 에 대상 의 정확 한 데이터 구조 가 있 도록 최선 을 다 합 니 다.하위 포인터 값 을 부모 포인터 변수 에 할당 할 때 컴 파 일 러 는 최선 을 다 해 조정 하지만 Void 포인터 값 등 이 발생 하면 컴 파 일 러 는 포인터 가 가리 키 는 위치 에 정확 한 대상 데이터 구조 가 있 음 을 보장 할 수 없다.이 단 계 는 문법 에 오류 가 없 으 면 바로 오 류 를 보고 하지 않 고 컴 파일 러 도 문제 가 발생 할 수 있 는 지 확인 할 수 없 으 며 이 지침 이 실제 적 으로 인용 을 풀 때 까지 기 다 려 야 오 류 를 보고 할 수 있다.자바 가 실 행 될 때 유형 이 있 습 니 다.대상 을 다른 유형의 변수 에 할당 할 때 실 행 될 때 유형 검 사 를 합 니 다.정확 한 유형 계승 관계 가 없 으 면 할당 타 임 스 가 잘못 될 수 있 습 니 다.
또한 자바 의 인터페이스 와 C++의 다 중 계승 을 비교 해 보면 인터페이스 가 실 행 될 때 C++다 중 계승 보다 시간 비용 이 훨씬 많은 것 을 발견 할 수 있다.그러나 C++다 중 계승 은 모든 부모 클래스 에 지침 을 추가 하고 컴 파일 러 는 컴 파일 할 때 더 많은 작업 을 해 야 합 니 다.자바 는 C++보다 더 강 한 언어 입 니 다.
C++의 다 중 상속 실현 과 자바 의 차이 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 C++다 중 상속 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Visual Studio에서 파일 폴더 구분 (포함 경로 설정)Visual Studio에서 c, cpp, h, hpp 파일을 폴더로 나누고 싶었습니까? 어쩌면 대부분의 사람들이 있다고 생각합니다. 처음에 파일이 만들어지는 장소는 프로젝트 파일 등과 같은 장소에 있기 때문에 파일...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.