2014-05-20 12 views
0

단일 추상 클래스의 상속자가되는 다른 유형의 클래스 표본을 저장하는 컨테이너를 올바르게 작성하는 데 문제가 있습니다. 레지스터 (컨테이너)는 추상 클래스의 유형을 가진 이러한 표본의 배열에 대한 포인터를 저장합니다. 표본에 포함 된 데이터에 액세스하려고 할 때마다 기본 클래스에서 찾을 수있는 부분 만 검색하는 데 성공합니다. 예를 들어 레지스터에 사용 된 오버로드 된 < <은 세 가지 상속자의 요소를 모두 포함하며 화면에 추상 클래스 파트 만 쓰고 존재하지 않는 것은 무시합니다. 이제는 문제가 제대로 저장된 요소를 인쇄하는 것과 관련이 있는지 또는 저장이 이미 부적절한 형식으로 수행되었는지는 알 수 없으므로 문제가 될 수 있습니다. 어떻게 제대로 수행해야합니까? 코드는 다음과 같습니다.이질적인 컨테이너 생성/관리 (C++)

class Register{ 
private: 
int elementNum; 
type * pData; 
friend std::ostream &operator<<(std::ostream & os,const Register &v); 
}; 
class type{ 
int a; 
int b; 
}; 
class type2: public type{ 
int c; 
int d; 
}; 

다른 두 개의 상속인은 type2와 같은 방식으로 작동합니다.

int main() 
    { 
     type2 A1(1,2,3,4); 
     type3 D1(4,5,6,7,8); 
     type4 H1(9,10,11,12,13); 
     std::cout<<A1<<D1<<H1<<endl; 
     Register R1; 
     R1.Add(0,A1); 
     R1.Add(1,D1); 
     R1.Add(2,H1); 
     R1.Display(); 
     R1.MaxLength(); 
     std::cout<<R1; 
     return 0; 
    } 

조작 레지스터에 < < : 만 < < 운영자 또는 레지스터로부터의 함수를 사용

std::ostream &operator<<(std::ostream & os,const Register &v){ 
    for(int i=0;i<v.elementNum;i++) 
    { 
     os<<v.pData[i]<<endl; 
    } 
    return os; 
} 

이 문제 만료 다음 메인의 일부이다. 편집 : 추가 기능의 구현 :

void Register::Add(int position,type& T){ 
    if(position<0||position>elementNum+1) 
     return; 
    type *pTemp = new type[elementNum+1]; 
    if(elementNum==0) 
    { 
     pTemp[0]=T; 
     delete[]pData; 
     pData=pTemp; 
    } 
    else 
    { 
     for(int i=0,j=0;j<elementNum+1;i++,j++) 
     { 
      if(position!=j) 
       pTemp[j]=pData[i]; 
      else 
      { 
       i--; 
       pTemp[j]=a; 
      } 
     } 
     delete[]pData; 
     pData=pTemp; 
    } 
    elementNum++; 
} 
+0

'Register :: Add'구현을 보여주십시오. 그것은 당신이 생각하는 것처럼 할 수는 없습니다. – aschepler

+0

그에 따라 업데이트되었습니다. – ClarkJohns

+0

아래에 답변에 추가되었습니다 :'sizeof (type)'바이트를 담을만큼 큰 배열 슬롯에만'type2'를 저장할 수 없습니다. 다형성을위한 포인터 배열이 필요합니다. – Jeff

답변

1

당신은 다형베이스에서 사용할 수있는 기본 클래스 또는 가상 메서드에 공통 공용 멤버에 액세스 할 수 있습니다.

또한 포인터/참조를 통해서만 가상 메서드에 액세스 할 수 있으며 일반적으로 pData과 같은 방식으로 서로 다른 클래스 인스턴스를 연속적으로 저장할 수 없습니다.

virtual std::ostream &type::dump(std::ostream &os) 멤버 메서드를 만들고 오버 라이드가 type2 등인 경우 각 오버라이드 메소드가 해당 하위 유형에만 해당하는 내용을 표시하도록 할 수 있습니다.

struct type { 
    virtual ostream &dump(ostream &os) { 
    os << a << " " << b << " "; 
    return os; 
    } 
    int a; 
    int b; 
}; 

struct type2 : type { 
    // Can use parent implementation AND use subtype-specific members: 
    ostream &dump(ostream &os) override { 
    type::dump(os); 
    os << c << " " << d << " "; 
    return os; 
    } 
    int c; 
    int d; 
}; 

// This class needs new "void Add(int pos, type &)" logic. 
struct Register { 
    int elementNum; 
    type *pData; // next hint: this is almost definitely not what you want. 
    type **pda; // probably better (need to use new/delete to make types) 
}; 

ostream &operator<<(ostream &os, Register const &v) { 
    for (int i = 0; i < v.elementNum; ++i) { 
    // Calls proper virtual method for each instance. 
    v.pData[i].dump(os); // XXX probably broken too 
    v.pda[i]->dump(os); // should look more like this 
    os << endl; 
    } 
} 
+0

등록 정보의 개인 요소로 프로토 타입을 넣어야한다고 미안합니다. 그것은 친구에게 맡겨져 있습니다, 나는 위에 게시 된 코드를 편집 할 것입니다. – ClarkJohns

+0

좋습니다, 당신의 방법은 잘 작동합니다, 고마워요! cout << R1은 이제 필요한 데이터의 모든 부분을 표시하지만 두 표본 사이에 어떤 종류의 메모리 쓰레기가 생깁니다 (영어로 정확한 단어를 모르는 경우). 첫 번째 표본이 올바르게 표시되면 다음 줄에 쓰레기가 있고 두 번째 표본도 올바르게 표시되며 프로그램이 종료되고 계속할 수 없습니다. 그 점에 대해 설명해 주시겠습니까? – ClarkJohns

+0

@ClarkJohns 당신의 다음 큰 문제는 배열에'type' 인스턴스를 다형 적으로 저장할 수 없다는 것입니다. 'type ** pData'를 가져야 만합니다.'type' /'typeN' 인스턴스에 * pointer *를 저장해야합니다. – Jeff

0
type *pTemp = new type[elementNum+1]; 

이 유형 type와 오브젝트의 배열을 할당한다. 객체는 유형을 변경할 수 없으며 배열의 요소를 바꿀 수없고 수정할 수만 있습니다. 따라서 Register 개체에는 파생 클래스의 개체가 전혀 포함되지 않으며 기본 클래스 형식의 개체 만 포함됩니다.

type **pTemp = new (type*[elementNum+1]); 

, 그것은 올바른 방법을 수행하는 방법은 배열과 원시 포인터를 피하고, 대신 컨테이너를 사용합니다 :

는 어려운 방법으로, 당신은 포인터의 배열을 필요 이종 객체의 배열을 얻으려면 스마트 포인터 :

class Register { 
public: 
    const type& get(int pos) const; 
    type& get(int pos); 

    void Add(int pos, const type& obj); 
    void Add(int pos, std::unique_ptr<type>&& ptr); 

    // ... 
private: 
    std::vector<std::unique_ptr<type>> m_data; 
}; 

그러나 어느 쪽이든, 당신은 당신의 기능 Add에서 무엇 포인터를 배치해야합니까?

void Register::Add(int position,type& T); 

전달 된 참조의 주소가 아마도 &T이 아닙니다. 그 물체가 파괴 될 때 누가 알 수 있습니다.그리고 new type(T)도 좋지 않습니다. 단지 T의 실제 유형을 무시하고 기본 유형의 객체를 만듭니다. 내가 Add() 두 오버로드에 넣어 위

class type { 
public: 
    using pointer = std::unique_ptr<type>; 
    virtual ~type(); 
    virtual pointer clone() const; 
}; 

type::pointer type::clone() const { 
    return pointer(new type(*this)); 
} 

type::pointer type2::clone() const { 
    return pointer(new type2(*this)); 
} 

: 그래서 당신은 아마 clone() 방법을 원하는 것, 때때로 "가상 복사 생성자"를 불렀다. 오히려 단지 객체보다, 이미 type::pointer이 일어날 경우

void Register::Add(int pos, const type& obj) { 
    if (pos<0) 
     return; 
    if (pos >= m_data.size()) 
     m_data.resize(pos+1); 
    m_data[pos] = obj.clone(); 
} 

다른 버전이 유용 할 수있다 : 개체-전달 버전은 같이 간다. 이 과부하를 사용하면 clone()에 아무 것도 할 필요없이 Register으로 이동할 수 있습니다.

+0

위로 코멘트보기 이것은 STL 사용을 낙담시키는 학교 과제로 보인다 ... – Jeff

+0

STL은 1998 년이 지나면 쓸모 없게되었으므로 문제가되지 않습니다. 그러나 표준 라이브러리의 일부를 의미하는 경우 조금 더 고통스럽게해야 할 것입니다. – aschepler

+0

죄송합니다. – Jeff