2014-05-17 4 views
3

의 내가 형 푸의 unique_ptr 객체를 집계 클래스 FooContainer 있다고 가정 해 봅시다깊은 복사 생성자

#include <vector> 
#include <memory> 

class FooContainer 
{ 
protected: 
std::vector<std::unique_ptr<Foo>> many; 
//other attributes 
public: 
FooCoontainer(const FooContainer&); 
//handling functions for Foo 
}; 

문제는 제대로 깊은 복사 생성자를 구현하고 어떤 구문입니다하는 방법입니다 그것을 위해. 간단히

FooContainer::FooContainer(const FooContainer& fc) 
{ 
many=fc.many; 

} 

를 할당하는 포인터를 복사하려고 시도하고있을 것 (고맙게도) unique_ptr에 대한 컴파일러에 의해 허용되지 않습니다. 그래서 나는 이와 같은 일을해야 할 것입니다.

FooContainer::FooContainer(const FooContainer& fc) 
{ 
many.reserve(fc.many.size()); 
for(int i=0;i<fc.many.size();i++) 
many.emplace_back(new Foo(*fc.many[i]));//assume that Foo has a copy constructor 
} 

이것을하는 방법입니까? 아니면 unique_ptr 대신 shared_ptr을 사용해야합니까?

나는 또한 추가 질문이 있습니다.
똑똑한 포인터 (그리고 위의 코드에서 보호 된)의 이유는 Foo의 하위 클래스 인 많은 개체 모음을 집계하는 BarContainer 클래스를 파생 시켰기 때문입니다. Bar의 처리가 Foo와 매우 유사하기 때문에이 접근법은 두 개의 개별 클래스에 비해 많은 중복 코드를 저장할 수 있습니다.

그러나,. BarContainer의 복사본 생성자는 문제가 있습니다. FooContainer의 콜 복사 생성자는 agead가되고 전체 Bar 대신 Foo 부분 만 복사합니다. 더 나쁜 경우, Bar의 가상 메소드를 호출하면 Foo 버전이 호출됩니다. 그래서이 동작을 재정의 할 방법이 필요합니다. 복사 생성자를 가상으로 만들 수 없습니다. 또한 Bar의 복사 생성자는 Foo 복사 생성자의 결과를 무시하고 올바른 복사를 수행 할 수 있지만 이는 매우 비효율적입니다.

그래서이 문제를 해결하는 가장 좋은 해결책은 무엇입니까?

+0

'unique_ptr 'copy-constructed를 반환 할 수있는'Foo' 및 그 서브 클래스에 가상'clone()'멤버 함수가 있어야합니다. '* '. – Oktalist

답변

2

또는 unique_ptr 대신 shared_ptr을 사용해야합니까?

딥 복사본이 필요한지 아니면 얕은 복사본이 좋을지에 따라 달라집니다 (하나의 변경 사항이 다른 복사본에도 표시됨).

그러나,. BarContainer의 복사본 생성자는 문제가 있습니다. FooContainer의 콜 복사 생성자는 agead가되고 전체 Bar 대신 Foo 부분 만 복사합니다. Foo 추상적없는 경우

class Foo { 
public: 
    Foo(Foo&&) = default; 
    Foo& operator=(Foo&&) = default; 
    virtual ~Foo() = 0; 
    virtual std::unique_ptr<Foo> clone() const = 0; 

protected: // or public if appropriate 
    Foo(const Foo&); 
    Foo& operator=(const Foo&); 
}; 

class Bar : public Foo { 
public: 
    virtual std::unique_ptr<Foo> clone() const; 
}; 

std::unique_ptr<Foo> Bar::clone() const { 
    return make_unique<Bar>(*this); 
} 

, 그것은 또한 clone()의 실제 구현을 할 것이다 :

일반적인 수정은 기본 클래스 가상 방법 clone을 제공하는 것입니다.

FooContainer::FooContainer(const FooContainer& fc) 
{ 
    many.reserve(fc.many.size()); 
    for (auto const& fptr : fc.many) 
     many.emplace_back(fptr->clone()); 
} 

내가 실수로 C++ 11 표준에서 분실 된 템플릿 함수 make_unique을 사용했지만, 공식 곧 될 것입니다.거의

template <typename T, typename... Args> 
std::unique_ptr<T> make_unique(Args&& ... args) { 
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); 
} 

(함께 unique_ptr는, make_unique는, shared_ptr는, make_sharedvector 당신을 의미하는 거대한 언어 개선을 완료합니다 : 컴파일러가 하나가없는 경우, 일부 헤더 파일에 자신을 넣어 간단합니다 new 또는 delete 키워드를 다시 필요로하지 마십시오.)

+1

대답 해 주셔서 감사합니다 예 깊은 사본이 필요합니다 나는'return std :: make_unique (* this); 그렇지. 실제로 복사해야하는 객체의 unique_ptr을 반환하는 것처럼 보입니다. –

+0

@AndreyPro : 그것에 대해 생각하십시오 :'T * p'가 주어 졌을 때, 어떻게'* p'가 가장 많이 파생 된 객체의 복사본을 만들 수 있습니까? 하위 객체입니까? 일반적인 대답은 없습니다. –

+0

'std :: make_unique'는 조금 조숙 한가? – Yakk