문제

다음 예제 코드를 고려하십시오.

class Foo
{
};

class Bar : public Foo
{
};

class FooCollection
{
protected:
    vector<shared_ptr<Foo> > d_foos;
};

class BarCollection : public FooCollection
{
public:
    vector<shared_ptr<Bar> > &getBars()
    {
        // return d_foos won't do here...
    }
};

현재 프로젝트에서 이와 같은 문제가 있습니다. 클라이언트 코드가 사용됩니다 BarCollection, 포인터를 저장합니다 Bars 안에 d_foos 선언된다 FooCollection. 이제 포인터 모음을 클라이언트 코드에 막대에 노출하고 싶습니다. 클라이언트 코드에 포인터 벡터에 액세스 할 수 있습니다. Foos와 이것을 포인터에 던져 넣습니다 Bar클라이언트 코드에 있지만 클라이언트가 알 필요가 없기 때문에 이것은 잘못 느껴집니다. Foo의 존재.

나는 또한 a를 정의 할 수있다 get() 객체를 검색하는 멤버 d_foos 그리고 그들을 던지지 만 이것은 매우 서투른 느낌입니다. 바람직하게는 D_Foos를 vector<shared_ptr<Bar> > &, 그러나 나는 이것을 할 수없는 것 같습니다.

또한 내 디자인이 명백한 잘못 일 수도 있습니다. 그래도 가장 자연스러운 솔루션으로 보였습니다 Bar 전문화입니다 Foo 그리고 BarCollection 전문화입니다 FooCollection 그리고 그들은 기능을 공유합니다.

구현할 좋은 솔루션을 제안해 주시겠습니까? getBars 안에 BarCollection 아니면 더 나은 디자인 대안?

편집하다:

내 디자인이 참으로 나빴다는 것이 밝혀졌습니다. Barcollection은 모든 foocollection의 기능을 요구 했음에도 불구하고 foocollection이 아닙니다. 아래의 답변을 기반으로하는 현재 솔루션 (훨씬 더 깨끗한 것)이 다음과 같습니다.

class Foo
{
};

class Bar : public Foo
{
};

template<class T>
class Collection
{
    vector<shared_ptr<T> > d_items;
};

typedef Collection<Foo> FooCollection;

class BarCollection : public Collection<Bar>
{
    // Additional stuff here.
};

모든 훌륭한 제안과 예에 감사드립니다!

도움이 되었습니까?

해결책

template<class T>
class MyContainer {
  vector<shared_ptr<T> > d_foos;
public:
  vector<shared_ptr<T> > & getVector();
};

class FooCollection : public MyContainer<Foo> {
};

class BarCollection : public MyContainer<Bar> {
};

다른 팁

회원 컨테이너 대신 컨테이너 클래스에서 반복자를 노출하는 것이 좋습니다. 그렇게하면 컨테이너 유형이 무엇인지는 중요하지 않습니다.

문제는 작동하지 않는 방식으로 두 가지 다른 독립적 인 독립적 인 다형성을 혼합하려고하는 것입니다. 템플릿의 컴파일 타임 유형-안전 다형성은 파생 된 유형의 기본 유형을 대체 할 수 없습니다. C ++의 템플릿 시스템은

class<Foo>

그리고

class<Bar>

한 가지 제안은 올바른 클래스로 내려갈 FOO 파생 어댑터를 만드는 것일 수 있습니다.

 template <class derived, class base>
 class DowncastContainerAdapter
 {
 private:
     std::vector< boost::shared_ptr<base> >::iterator curr;
     std::vector< boost::shared_ptr<base> >::const_iterator end;
 public:
     DowncastContainerAdapter(/*setup curr & end iterators*/)
     {
         // assert derived actually is derived from base
     }

     boost::shared_ptr<derived> GetNext()
     {
         // increment iterator
         ++curr;
         return dynamic_cast<base>(*curr);
     }

     bool IsEnd()
     {
         return (curr == end);
     }
 };

참고이 클래스는 반복자와 동일한 문제를 겪게되며 벡터의 작업은이 클래스를 무효화 할 수 있습니다.

또 다른 생각

당신은 그것을 깨닫지 못할 수도 있지만 Foo의 벡터를 반환하는 것은 완벽하게 괜찮을 수 있습니다. 바 사용자는 이미 bar.h를 포함하여 foo에 대한 모든 지식을 가지고 있어야합니다. BAR이 FOO에서 물려 받기 때문에 Foo.h를 통해 수업에 대한 전적인 지식이 있어야합니다. 가능한 경우 Foo (또는 Super Class of Foo)를 인터페이스 클래스로 만들고 해당 인터페이스 클래스로 포인터의 벡터를 전달하는 것이 위의 솔루션을 사용하는 것보다 제안합니다. 이것은 꽤 일반적으로 보이는 패턴이며 내가 힘든 솔루션을 제시 한 눈썹을 올리지 않을 것입니다. :). 그런 다음 다시 당신은 당신의 이유가있을 수 있습니다. 모두 행운을 빕니다.

문제는 왜 그렇게 하시겠습니까? 사용자에게 바에 대한 포인터 모음을 제공하면 막대 만 있다고 가정하므로 내부적으로 포인터를 컬렉션에 저장하는 것은 말이되지 않습니다. FOO에 대한 포인터 모음에 FOO의 다른 하위 유형을 저장하는 경우, 바에 포인터 모음으로 반환 할 수는 없습니다. 첫 번째 경우 (막대 만 있다는 것을 알고 있습니다) 위에서 제안한대로 템플릿 접근 방식을 사용해야합니다. 그렇지 않으면, 당신은 당신이 정말로 원하는 것을 다시 생각해야합니다.

이것을 foo / bar에 템플릿으로 템플릿으로 교체 할 수 있습니까?

class Collection<T> {
protected:
    vector<shared_ptr<T> > d_foos;
};

typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;

특별한 필요가 있습니까? BarCollection 로부터 나오다 FooCollection? 일반적으로 a BarCollection 아니다FooCollection, 일반적으로 FooCollection a BarCollection. 예를 들어:

BarCollection *bc = new BarCollection();
FooCollection *fc = bc; // They are derived from each other to be able to do this
fc->addFoo(Foo());      // Of course we can add a Foo to a FooCollection

이제 우리는 a를 추가했습니다 Foo a BarCollection. 만약 BarCollection 새로 추가 된이 요소에 액세스하려고 시도하고 그것이 Bar, 모든 종류의 못생긴 일이 일어날 것입니다.

따라서 일반적으로 이것을 피하고 싶고 컬렉션 클래스가 서로 파생되지 않습니다. 또한보십시오 질문 ~에 대한 캐스팅 용기 이 주제에 대한 더 많은 답변을위한 파생 유형 ...

우선, 이야기합시다 shared_ptr. 당신은 다음을 알고 있습니까 : boost::detail::dynamic_cast_tag ?

shared_ptr<Foo> fooPtr(new Bar());
shared_ptr<Bar> barPtr(fooPtr, boost::detail::dynamic_cast_tag());

이것은 매우 편리한 방법입니다. 표지 아래에서 그것은 단지 a dynamic_cast, 거기에서 멋진 것은 없지만 쉬운 표기법입니다. 계약은 클래식 한 것과 동일합니다. Bar (또는 그것에서 파생 된), 당신은 널 포인터를 얻습니다.

질문으로 돌아 가기 : 나쁜 코드.

BarCollection 아닙니다 FooCollection, 언급했듯이, 당신은 포인터의 벡터에 다른 요소를 소개 할 수 있기 때문에 곤경에 처해 있습니다. Bar 하나.

그래도 이것은 문제를 넘어서기 때문에 확장하지 않을 것입니다. 우리는 (대답하려는 사람들로서) 우리 자신을 제한해야한다고 생각합니다.

참조를 전달할 수는 없지만 통과 할 수 있습니다. View.

기본적으로, a View a로 작용하는 새로운 대상입니다 Proxy 오래된 사람에게. 비교적 사용이 쉽습니다 boost.iterators 예에서.

class VectorView
{
  typedef std::vector< std::shared_ptr<Foo> > base_type;

public:
  typedef Bar value_type;
  // all the cluttering

  class iterator: boost::iterator::iterator_adaptor<
    iterator,
    typename base_type::iterator,
    std::shared_ptr<Bar>
  >
  {
    typename iterator_adaptor::reference dereference() const
    {
      // If you have a heart weakness, you'd better stop here...
      return reinterpret_cast< std::shared_ptr<Bar> >(this->base_reference());
    }
  };

  // idem for const_iterator

  // On to the method forwarding
  iterator begin() { return iterator(m_reference.begin()); }

private:
  base_type& m_reference;
}; // class VectorView

여기서 진짜 문제는 물론입니다 reference 조금. a NEW shared_ptr 객체는 쉽고 수행 할 수 있습니다 dynamic_cast 필요에 따라. a reference ~로 ORIGINAL shared_ptr 그러나 필요한 유형으로 해석되는 것은 ... 실제로 코드에서보고 싶은 것이 아닙니다.

메모:
boost.fusion을 사용하는 것보다 더 잘할 수있는 방법이있을 수 있습니다. transform_view 수업이지만 나는 그것을 알아낼 수 없었습니다.

특히 사용 transform_view 나는 얻을 수있다 shared_ptr<Bar> 그러나 나는 얻을 수 없다 shared_ptr<Bar>& 반복기를 불신 할 때, 기본에 대한 참조를 반환하는 것이 유일한 사용이라는 점을 감안할 때 성가신 것입니다. vector (그리고가 아니라 const_reference)는 실제로의 구조를 수정하는 것입니다 vector 그리고 그것이 포함 된 물체.

노트 2:
리팩토링을 고려하십시오. 거기에는 훌륭한 제안이있었습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top