2013-06-29 5 views
2

나는 C++에서 어떤 일이 일어날지를 보려고 노력했다. 비슷한 방법으로 객체의 배열을 "끊으려고"Java에서 시도 할 수있다.C++에서 배열을 업 캐스트 한 다음 다른 하위 유형을 넣으려고합니까 (Java ArrayStoreException에서 영감을 얻음)?

Java에서 우리는 Double [] 형식의 배열을 가질 수 있습니다. 예를 들어, Double은 Number의 하위 클래스이므로 Number []로 업 캐스트하고 Array에 Number의 다른 하위 클래스를 추가하려고합니다 (예 : 정수. 코드는 컴파일되지만 런타임에 ArrayStoreException이 발생합니다. Integer 유형은 실제 배열 유형 (런타임시 Double 일 수 있음)과 비교하여 검사되기 때 문에 불일치가 발생하기 때문입니다. 코드는 다음과 같을 수 있습니다.

Double[] ds = new Double[12]; 
Number[] ns = ds; 
ns[0] = 2.3;    // OK 
ns[1] = new Integer(1); // compiles, but we have ArrayStoreException in runtime 

그래서 저는 C++을 어떻게 생각합니까? 같은 트릭을 수행 할 수 있습니까? 런타임에 어떤 일이 발생합니까?

다음은 내가 시도한 코드와 출력입니다.

#include <iostream> 

class A 
{ 
public: 
    A(int v = 0): val(v) {} 
    virtual void g() {std::cout << val << " in A\n";} 
    void setVal(int v) {val = v;} 
protected: 
    int val; 
}; 

class B : public A 
{ 
public: 
    virtual void g() {std::cout << val << " in B\n";} 
}; 

class C : public A 
{ 
public: 
    C(int v = 0): A(v) {} 
    virtual void g() {std::cout << val << " in C\n";} 
private: 
    int stuff[10]; 
}; 


void f(A* as) 
{ 
    as[1] = *(new A(12)); 
} 

void f2(A* as) 
{ 
    as[1] = *(new C(22)); 
} 

int main() 
{ 
    A* bs = new B[5]; 
    for (int i=0 ; i<5; ++i) 
    { 
     bs[i].setVal(i); 
     bs[i].g(); 
    } 

    std::cout << std::endl; 

    f(bs); 
    for (int i=0 ; i<5; ++i) 
    { 
     bs[i].g(); 
    } 

    std::cout << std::endl; 

    f2(bs); 
    for (int i=0 ; i<5; ++i) 
    { 
     bs[i].g(); 
    } 
} 

출력 :

0 in B 
1 in B 
2 in B 
3 in B 
4 in B 

0 in B 
12 in B 
2 in B 
3 in B 
4 in B 

0 in B 
22 in B 
2 in B 
3 in B 
4 in B 

는 C 데이터 만의 경우 모두 예상대로, A 또는 C를 만든 다음 B의 어레이 내부를 복사 않는 복사 데이터 (것을보기 A의 일부가 복사되고 복사 된 요소 다음에 메모리가 손상되지 않습니다.)하지만 B의 메소드가 선택됩니다. 즉, vptr이 복사되지 않았 음을 의미합니다.

그래서 제 질문은 다음과 같습니다

  • 는 그 vptr에서이 기본 할당 연산자에 복사되지 않습니다 같아요. 그렇지? 그것은 우리가 호출 한 B의 메서드를 가지고 있지만 C 객체의 데이터를 가질 수있는 유일한 이유입니까?

  • 일부 런타임 오류가 발생하거나 예기치 않은 일이 발생할 수 있습니까? 내가 의미하는 바는 의 배열이 Bs이지만 A 또는 C의 객체 (은 B가 아니거나 하위 유형이 B이 아닙니다), B에 "외계인"인 객체입니까?

  • 또는 어쩌면 C++ 언어가 명시 적으로 또는 암시 적으로 (예 : ArrayStoreException을 명시 적으로 발생시킬 때 Java와 같은) 이러한 기능을 수행 할 수 없다는 보장이 있습니까?

는 UPD :

A* bs = new B[5]; 
가 실제로 B 방법 (의 런타임 선택에 중점을 넣어, 마지막 순간에이 줄을 변경

그것을하지 말았어야,이 방법 때문에 분명하다 가상입니다.) 처음에는 B* bs = new B[5];이고 출력은 동일했습니다.

+1

Java의'Double [] ds'의 C++ 아날로그는'Double * ds []', 즉 포인터의 배열입니다 ** ** 객체의 배열이 아닙니다. –

답변

1

사실, A * bs = new B [5]를 할 때, 당신은 뭔가 잘못하고있는 것입니다. B 요소의 크기가 B 요소와 같지 않으면 문제가 발생할 수 있습니다. B *를 A *로 상향 조정할 수 있기 때문에 변환은 안전합니다. 단 하나의 요소 만 있다면.

A의 길이는 32 비트이고 B의 길이는 64입니다. 그러면 bs 배열을 검색 할 때 이상한 일이 생깁니다. (bs [1]은 첫 번째 B 요소의 끝 부분입니다.)

가상 상속이 여러 개 있고 컴파일러에서 뾰족한 주소를 변경해야하는 경우 첫 번째 요소에 대해서만 수행됩니다. 그래서

: 필요한 경우

  1. 아무것도 복사 할 수 있고, 첫 번째 요소의 포인터 주소가 변경됩니다.

  2. 예, B가 A보다 크고 정의되지 않은 동작으로 멋진 세계에 올라서 기 위해서 일부 요소를 B에 추가하기 만하면됩니다. 또한 B 오브젝트 내에 C 오브젝트의 A 부분을 복사 할 수도 있습니다.

주의 : 같은 배열에 약간의 B와 C를 관리 할 경우 A A ** (또는 표준 : 무엇이든) 다음은 안전하고 사용할 수 있습니다. (그러나 당신은 아마 그것을 이미 알고 있습니다).

1

1) 예, vptr은 할당 연산자에 복사되지 않습니다. 대입 연산자는 객체의 런타임 유형을 변경하지 않습니다.

2) A* bs = new B[5];입니다. BA의 데이터가 포함되어 있으면 어떻게 될까요? 그런 다음 sizeof(B) > sizeof(A)을 입력하고 bs[i]을 통해 항목에 액세스하면 정의되지 않은 동작이 발생합니다 (시스템에서 세그먼트 오류).

+0

2) 실제로 마지막 순간에이 라인을 변경하여 B 메소드의 런타임 선택에 중점을 두었습니다. (수행하지 말아야 할 것은 메소드가 가상이기 때문에 분명합니다). 나는 처음에'B * bs = new B [5];를 가지며 결과물은 같았다. –