C++에서 기본 클래스 와 파생 클래스 의 전환 및 가상 클래스 를 자세히 설명 합 니 다.

10115 단어 C++
C++기본 클래스 와 파생 클래스 의 전환
공용 상속,사유 상속 과 보호 상속 에서 공용 상속 만 기본 클래스 의 특징 을 잘 보존 할 수 있다.이 는 구조 함수 와 석조 함 수 를 제외 한 기본 클래스 의 모든 구성원 을 보존 하고 기본 클래스 의 공용 또는 보호 구성원 의 방문 권한 은 파생 클래스 에서 모두 그대로 유지 된다.파생 클래스 외 에 기본 클래스 의 공용 구성원 함 수 를 호출 하여 기본 클래스 의 개인 구성원 에 게 접근 할 수 있 습 니 다.따라서 공용 파생 류 는 기류 의 모든 기능 을 가지 고 모든 기류 가 실현 할 수 있 는 기능 을 가 지 며 공용 파생 류 는 모두 실현 할 수 있다.공용 파생 류(사유 또는 보호 파생 류)가 아 닌 기본 클래스 의 모든 기능 을 실현 할 수 없습니다(예 를 들 어 파생 류 외 에 기본 클래스 의 공용 구성원 함수 가 기본 클래스 의 개인 구성원 에 접근 할 수 없습니다).따라서 공용 파생 류 만 이 기본 적 인 하위 유형 으로 기본 적 인 기능 을 온전 하 게 계승 했다.
서로 다른 유형의 데이터 간 에 일정한 조건 하에 서 유형의 변환 을 할 수 있다.예 를 들 어 정형 데 이 터 는 쌍 정밀도 형 변 수 를 부여 할 수 있 고 값 을 부여 하기 전에 정형 데 이 터 를 먼저 쌍 정밀도 형 데이터 로 변환 할 수 있 지만 정형 데 이 터 를 지침 변수 에 부여 할 수 없다.이러한 서로 다른 유형의 데이터 간 의 자동 변환 과 할당 을 할당 호 환 이 라 고 합 니 다.현재 토론 하고 자 하 는 문 제 는 기본 클래스 와 파생 클래스 대상 간 에 도 할당 호 환 관계 가 있 는 지,유형 간 전환 을 할 수 있 는 지 하 는 것 이다.
대답 은 할 수 있어.기본 클래스 와 파생 클래스 대상 사이 에는 할당 호 환 관계 가 있 으 며,파생 클래스 에는 기본 클래스 에서 계승 하 는 구성원 이 포함 되 어 있 기 때문에 파생 클래스 의 값 을 기본 클래스 대상 에 부여 할 수 있 으 며,기본 클래스 대상 을 사용 할 때 하위 클래스 대상 으로 대체 할 수 있다.구체 적 으로 다음 과 같은 몇 가지 측면 에 나타난다.
1)파생 대상 은 기본 대상 에 게 값 을 부여 할 수 있다.
하위 클래스(즉 공용 파생 클래스)대상 으로 기본 클래스 대상 에 값 을 부여 할 수 있 습 니 다....와 같다

  A a1; //    A  a1
  B b1; //   A      B   b1
  a1=b1; //    B  b1     a1  
값 을 부여 할 때 파생 류 자신의 구성원 을 버리다.인재 소 용

실제로 할당 이란 데이터 구성원 에 대한 할당 일 뿐 구성원 함수 에 할당 문제 가 존재 하지 않 습 니 다.
값 을 부여 한 후 대상 a1 을 통 해 파생 대상 b1 의 구성원 을 방문 하려 고 시도 해 서 는 안 됩 니 다.b1 의 구성원 과 a1 의 구성원 이 다 르 기 때 문 입 니 다.age 가 파생 클래스 B 에 추 가 된 공용 데이터 구성원 이 라 고 가정 하고 아래 의 용법 을 분석 합 니 다.
    a1.age=23;  //오류,a1 에는 파생 클래스 에 추 가 된 구성원 이 포함 되 어 있 지 않 습 니 다.
    b1.age=21;  //정확,b1 에는 파생 류 에 추 가 된 구성원 이 포함 되 어 있 습 니 다.
하위 유형의 관 계 는 단 방향 적 이 고 거 스 를 수 없 음 을 주의해 야 한다.B 는 A 의 하위 유형 이 므 로 A 는 B 의 하위 유형 이 라 고 할 수 없다.하위 클래스 대상 으로 만 기본 클래스 대상 에 값 을 부여 할 수 있 고 기본 클래스 대상 으로 하위 클래스 대상 에 값 을 부여 할 수 없다.이 유 는 분명 하 다.기본 클래스 대상 은 파생 클래스 의 구성원 을 포함 하지 않 기 때문에 파생 클래스 의 구성원 에 게 값 을 부여 할 수 없다.같은 이치 로 같은 기류 의 다른 파생 류 대상 간 에 도 값 을 부여 할 수 없다.
2)파생 대상 은 기본 대상 을 대체 하여 기본 대상 에 대한 인용 을 할당 하거나 초기 화 할 수 있다.
기본 클래스 A 대상 a1 이 정의 되 어 있 으 면 a1 의 인용 변 수 를 정의 할 수 있 습 니 다.

  A a1; //    A  a1
  B b1; //       B  b1
  A& r=a1; //    A       r,  a1     
이 때 인용 변수 r 는 a1 의 별명 이 고 r 와 a1 은 같은 저장 소 를 공유 합 니 다.하위 클래스 대상 으로 인용 변수 r 를 초기 화하 고 위의 마지막 줄 을 바 꿀 수도 있 습 니 다.

  A& r=b1; //    A       r,     B  b1     
또는 위의 세 번 째 줄"A&r=a1;"을 보류 합 니 다.r 에 대한 재 할당:

  r=b1; //    B  b1 a1     r  
이 때 r 는 b1 의 별명 이 아니 라 b1 과 같은 저장 소 를 공유 하지 않 습 니 다.이것 은 b1 의 기본 클래스 부분의 별명 일 뿐 r 는 b1 의 기본 클래스 부분 과 같은 저장 소 를 공유 하고 r 는 b1 과 같은 시작 주 소 를 가지 고 있 습 니 다.
3)함수 의 매개 변수 가 기본 대상 이나 기본 대상 의 참조 라면 해당 하 는 실제 참 조 는 하위 대상 을 사용 할 수 있다.
함수 가 있 으 면:

  fun: void fun(A& r) //    A        
  {
    cout<<r.num<<endl;
  } //            num
함수 의 형 삼 은 클래스 A 의 대상 의 인용 변수 로 원래 실 삼 은 A 류 의 대상 이 어야 한다.하위 클래스 대상 과 파생 클래스 대상 의 할당 이 호 환 되 기 때문에 파생 클래스 대상 은 자동 으로 유형 을 바 꿀 수 있 습 니 다.fun 함 수 를 호출 할 때 파생 클래스 B 의 대상 b1 을 실제 참조 로 할 수 있 습 니 다.

   fun(b1);
출력 클래스 B 의 대상 b1 의 기본 데이터 구성원 num 의 값 입 니 다.
이전 과 마찬가지 로 fun 함수 에 서 는 파생 클래스 의 기본 구성원 의 값 만 출력 할 수 있 습 니 다.
4)파생 대상 의 주 소 는 기본 대상 을 가리 키 는 포인터 변 수 를 부여 할 수 있다.즉,기본 대상 을 가리 키 는 포인터 변 수 는 파생 대상 을 가리 킬 수 있다.
[예]기본 클래스 Student(학생)를 정의 하고 Student 류 의 공용 파생 클래스 Graduate(대학원)를 정의 하여 기본 대상 을 가리 키 는 지침 으로 데 이 터 를 출력 합 니 다.이 예 는 기본 대상 을 가리 키 는 지침 으로 파생 대상 을 가리 키 고 프로그램의 길 이 를 줄 이기 위해 각 유형 에 적은 구성원 만 설치 한 다 는 것 을 설명 한다.학생 류 는 num(학 번),name(이름),score(성적)3 개 데이터 멤버 만 설정 하고 Graduate 류 는 1 개 데이터 멤버 pay(임금)만 추가 합 니 다.프로그램 은 다음 과 같 습 니 다:

#include <iostream>
#include <string>
using namespace std;
class Student//  Student 
{
public:
  Student(int, string,float); //      
  void display( ); //      
private:
  int num;
  string name;
  float score;
};
Student::Student(int n, string nam,float s) //      
{
  num=n;
  name=nam;
  score=s;
}
void Student::display( ) //      
{
  cout<<endl<<"num:"<<num<<endl;
  cout<<"name:"<<name<<endl;
  cout<<"score:"<<score<<endl;
}
class Graduate:public Student //       Graduate
{
public:
 Graduate(int, string ,float,float); //      
 void display( ); //      
private:
 float pay; //  
};
//      
Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ }
void Graduate::display() //      
{
  Student::display(); //  Student  display  
  cout<<"pay="<<pay<<endl;
}
int main()
{
  Student stud1(1001,"Li",87.5); //  Student   stud1
  Graduate grad1(2001,"Wang",98.5,563.5); //  Graduate   grad1
  Student *pt=&stud1; //    Student         stud1
  pt->display( ); //  stud1.display  
  pt=&grad1; //    grad1
  pt->display( ); //  grad1.display  
}
다음은 절차 에 대한 분석 이 중요 하 니 잘 읽 고 생각 하 세 요.
많은 독자 들 은 파생 클래스 에 같은 이름 의 display 구성원 함수 가 두 개 있 는데 같은 이름 으로 덮어 쓰 는 규칙 에 따라 파생 클래스 Graduate 대상 의 display 함수 가 호출 되 어야 한다 고 생각 할 것 이다.Graduate:display 함 수 를 실행 하 는 과정 에서 Student:display 함수,출력 num,name,score 를 호출 한 다음 에 pay 의 값 을 출력 해 야 한다.
사실 이런 추론 은 잘못된 것 입 니 다.먼저 프로그램의 출력 결 과 를 보십시오.

num:1001
name:Li
score:87.5

num:2001
name:wang
score:98.5
앞의 3 줄 은 학생 stud 1 의 데이터 이 고,뒤의 3 줄 은 대학원 grad 1 의 데이터 이 며,pay 의 값 을 출력 하지 않 았 다.
문 제 는 pt 가 Student 류 대상 을 가리 키 는 포인터 변수 입 니 다.grad 1 을 가리 키 더 라 도 실제 pt 는 grad 1 에서 기본 적 인 계승 부분 을 가리 키 고 있 습 니 다.
기본 대상 을 가리 키 는 지침 을 통 해 파생 류 의 기본 구성원 만 방문 할 수 있 고 파생 류 가 증가 하 는 구성원 은 방문 할 수 없다.그래서 pt->display()는 파생 클래스 Graduate 대상 에 추 가 된 display 함수 가 아니 라 기본 클래스 의 display 함수 이기 때문에 대학원 grad 1 의 num,name,score 3 개의 데이터 만 출력 합 니 다.
대학원 grad 1 의 pay 를 포인터 로 출력 하려 면 파생 대상 을 가리 키 는 포인터 변 수 를 따로 설정 하여 grad 1 을 가리 키 고 ptr->display()로 파생 대상 의 display 함 수 를 호출 할 수 있 습 니 다.불편 합 니 다.
이 예 를 통 해 알 수 있 듯 이 기본 대상 을 가리 키 는 포인터 변수 로 하위 대상 을 가리 키 는 것 은 합 법 적 이 고 안전 하 며 컴 파일 상의 오류 가 발생 하지 않 습 니 다.그러나 응용 에 있어 서 사람들의 희망 을 완전히 만족 시 키 지 못 하고 사람들 은 가끔 기본 지침 을 사용 하여 기본 클래스 와 하위 대상 의 구성원 을 호출 할 수 있 기 를 원한 다.이 정도 면 프로그래머 들 이 편 할 것 이다.후속 장 에서 이 문 제 를 해결 할 것 이다.방법 은 허 함수 와 다 태 성 을 사용 하 는 것 이다.
C++가상 기본 클래스 상세 설명
다 중 계승 시 이름 충돌 이 발생 하기 쉽다.모든 종류의 구성원 변수 와 구성원 함 수 를 조심스럽게 서로 다른 이름 으로 명명 하 더 라 도 이름 충돌 은 여전히 발생 할 수 있다.예 를 들 어 매우 전형 적 인 마름모꼴 계승 차원 이다.다음 그림 에서 보 듯 이:

클래스 A 는 클래스 B 와 클래스 C 를 파생 시 키 고 클래스 D 는 클래스 B 와 클래스 C 를 계승 합 니 다.이때 클래스 A 의 구성원 변수 와 구성원 함수 가 클래스 D 에 계승 되 어 두 가지 가 되 었 습 니 다.하 나 는 A->B->D 에서 왔 고 다른 하 나 는 A->C->D 에서 왔 습 니 다.
한 파생 류 에서 간접 기류 의 여러 명의 동명 이인 구성원 을 보류한다.비록 서로 다른 구성원 변수 에 각각 다른 데 이 터 를 저장 할 수 있 지만 대부분 상황 에서 이것 은 불필요 하 다.왜냐하면 여러 구성원 변 수 를 보류하 는 것 은 비교적 많은 저장 공간 을 차지 할 뿐만 아니 라 생명 명 충돌 도 발생 하기 쉬 울 뿐만 아니 라 이런 수요 도 매우 적기 때문이다.
이 문 제 를 해결 하기 위해 C++는 가상 기 류 를 제공 하여 파생 류 에서 간접 기 류 의 한 구성원 만 유지 하도록 한다.
가상 기본 클래스 를 설명 하려 면 계승 방식 앞 에 virtual 키 워드 를 추가 해 야 합 니 다.아래 의 예 를 보십시오.

#include <iostream>
using namespace std;
class A{
protected:
  int a;
public:
  A(int a):a(a){}
};
class B: virtual public A{ //     
protected:
  int b;
public:
  B(int a, int b):A(a),b(b){}
};
class C: virtual public A{ //     
protected:
  int c;
public:
  C(int a, int c):A(a),c(c){}
};
class D: virtual public B, virtual public C{ //     
private:
  int d;
public:
  D(int a, int b, int c, int d):A(a),B(a,b),C(a,c),d(d){}
  void display();
};
void D::display(){
  cout<<"a="<<a<<endl;
  cout<<"b="<<b<<endl;
  cout<<"c="<<c<<endl;
  cout<<"d="<<d<<endl;
}
int main(){
  (new D(1, 2, 3, 4)) -> display();
  return 0;
}
실행 결과:

a=1
b=2
c=3
d=4
이 예 에서 우 리 는 가상 기 류 를 사 용 했 습 니 다.파생 류 D 에서 구성원 변수 a 의 복사 만 있 기 때문에 display()함수 에서 a 를 직접 방문 할 수 있 습 니 다.클래스 이름과 도 메 인 해석 자 를 추가 하지 않 아 도 됩 니 다.
파생 류 D 의 구조 함수 에 주의 하 세 요.기 존의 용법 과 다 릅 니 다.기 존 에는 파생 류 의 구조 함수 에서 직접 기본 클래스 초기 화 를 책임 지고 직접 기본 클래스 에서 간접 기본 클래스 초기 화 를 책임 져 야 했다.현재 가상 기본 클래스 는 파생 클래스 에서 하나의 구성원 변수 만 있 기 때문에 이 구성원 변수 에 대한 초기 화 는 파생 클래스 에서 직접 해 야 합 니 다.마지막 파생 클래스 에서 가상 클래스 를 직접 초기 화하 지 않 고 가상 클래스 의 직접 파생 클래스(예 를 들 어 클래스 B 와 클래스 C)에서 가상 클래스 를 초기 화하 지 않 으 면 클래스 B 와 클래스 C 의 구조 함수 에서 가상 클래스 에 대해 서로 다른 초기 화 파 라 메 터 를 제시 하여 갈등 이 생 길 수 있 습 니 다.따라서 마지막 파생 류 에 서 는 직접 기 류 를 초기 화 하 는 것 뿐만 아니 라 허 기 류 를 초기 화 하 는 것 도 책임 져 야 한다.
어떤 독자 들 은 클래스 D 의 구조 함수 가 초기 화 표를 통 해 가상 기류 의 구조 함수 A 를 조 정 했 고 클래스 B 와 클래스 C 의 구조 함수 도 초기 화 표를 통 해 가상 기류 의 구조 함수 A 를 조 정 했 는데 이런 가상 기류 의 구조 함수 가 3 번 호출 되 지 않 았 겠 는가?여러분 은 지나치게 걱정 할 필요 가 없습니다.C+컴 파일 시스템 은 마지막 파생 류 가 가상 기류 의 구조 함수 에 대한 호출 만 실행 하고 가상 기류 의 다른 파생 류(예 를 들 어 클래스 B 와 클래스 C)가 가상 기류 의 구조 함수 에 대한 호출 을 무시 하면 가상 기류 의 데이터 구성원 이 여러 번 초기 화 되 지 않도록 보장 합 니 다.
마지막 으로 주의:허 기류 가 파생 류 에서 한 번 만 계승 할 수 있 도록 이 기류 의 모든 직접 파생 류 에서 허 기류 로 성명 해 야 한다.그렇지 않 으 면 기류 에 대한 여러 차례 계승 이 나타 날 수 있다.
이 를 통 해 알 수 있 듯 이 다 중 상속 을 사용 할 때 는 매우 조심해 야 하 며,종종 이의 성 문제 가 발생 할 수 있다.위의 예 는 간단 하 다.만약 에 파생 된 차원 이 좀 더 많 으 면 다 중 계승 이 더욱 복잡 하고 프로그래머 가 사람 에 게 빠 지기 쉬 우 며 프로그램의 작성,디 버 깅 과 유지 보수 작업 이 더욱 어려워 질 것 이다.따라서 많은 프로그래머 들 이 프로그램 에서 다 중 상속 을 사용 하 는 것 을 제창 하지 않 고 비교적 간단 하고 이의 성 이 나타 나 지 않 는 상황 이나 실제 필요 할 때 만 다 중 상속 을 사용 하고 단일 상속 으로 해결 할 수 있 는 문 제 는 다 중 상속 을 사용 하지 않 는 다.이 때문에 C++이후 의 많은 대상 을 대상 으로 하 는 프로 그래 밍 언어(예 를 들 어 자바,Smalltalk,C\#,PHP 등)는 다 중 계승 을 지원 하지 않 습 니 다.

좋은 웹페이지 즐겨찾기