2013-07-29 6 views
3
#include <iostream> 
#include <vector> 

using namespace std; 

class A { 
    private: 
    int number; 
    public: 
    A() {number = 0;} 
    A(int nr) {number = nr;} 
    //A(const A& rhand) {this->number = rhand.number;} 
    int get_number() const {return this->number;} 
    A& operator=(const A& rhand) { 
     this->number = rhand.number; 
     return (*this); 
    } 
}; 

class B { 
    private: 
    vector<A*>* content; 
    vector<A*>::iterator currA; 
    bool currA_valid; 
    public: 
    B() { 
     content = new vector<A*>; 
     currA = content->begin(); 
     currA_valid = false; 
    } 
    void push_A(A* nA) {content->push_back(nA); currA_valid = false;} 
    void push_A(A nA) {content->push_back(&nA); currA_valid = false;} 
    A get_A() { 
     if(!currA_valid) { 
     currA = content->begin(); 
     if(!content->empty()) { 
      currA_valid = true; 
     } 
     } 
     if(currA == content->end() || this->content->empty()) { 
     currA = content->begin(); 
     return A(); 
     } 
     else { 
     A result(**currA); 
     ++currA; 
     return result; 
     } 
    } 
}; 

int main() 
{ 
    B container; 

    A* a1 = new A(1); 
    cout << a1->get_number() << endl; 
    A a2(2); 
    cout << a2.get_number() << endl; 

    container.push_A(a1); 
    container.push_A(a2); 


    A tmp; 
    while((tmp = container.get_A()).get_number() != 0) 
    cout << "Inhalt tmp: " << tmp.get_number() << endl; 

    return 0; 
} 

최근에 나는이 코드 스 니펫으로 바뀌 었습니다.왜이 복사 생성자가 필요한가

본질적 클래스 B는 실제 코드 A의 클래스 A.

물체 컨테이너 훨씬 크다이며 타입 A의 동일한 오브젝트 공간을 절약하기 위해 컨테이너에 여러 번 정도 나타나는, B는 A에 대한 포인터 만 저장합니다.

B :: push 함수는 A 유형의 객체를 컨테이너에 공급합니다.

그들은 A.

주요 기능의 끝에있는 동안 루프의 포인터와 값 과부하 난 (가지 iostream 개체에 사용되는 스트리밍 연산자 등) 원하던.

B의 반복자 "currA"는 B :: get_A() 함수 호출에서 마지막으로 반환 된 요소를 추적하므로 해당 함수를 연속적으로 호출하면 B의 모든 A 객체가 끝날 때까지 반환됩니다 에 도달했습니다. 이 경우 내부 반복자가 재설정되고 내부 무효화 플래그 (이 경우 A :: number가 단순함)가있는 객체 A가 반환됩니다.

이 프로그램의 출력은 다음과 같을 수있다 :

1 
2 
Inhalt tmp: 1 //content of a1 
Inhalt tmp: 4620996 //content of a2 

주요 기능은 두 개체 ± 1 (1)에서 A * (A2)의 인스턴스 (2).

A :: get_number()를 테스트하려면 내부 값이 모두 표시되어야합니다. 둘 다 예상대로 작동합니다. 그러나 컨테이너에 두 가지를 모두 넣은 다음 다시 가져온 후에는 a1 만 제대로 표시됩니다. a2의 내용은 임의의 난수를 보여줍니다.

먼저 생각에서, 나는 포인터의 일부 실수를하지만, 하나는 선언하면 문제가 해결 될 것을 증명과 같은 클래스 A의 복사 생성자 정의 : 지금까지

A(const A& rhand) {this->number = rhand.number;} 

을 필자는 복사 생성자가 제공되지 않는다면 C++ 컴파일러에 의해 암시 적으로 정의되며, 클래스가 얕은 복사본을 피하기 위해 포인터를 멤버로 가질 때이를 구현하는 것이 좋습니다. 그러나이 경우 A는 int 만 갖는다.

또한 B :: get_A()를 제거하고 다른 방법으로 컨테이너 내용을 가져 와서 코드를 단순화하려고했습니다. 문제는 기본 생성자를 구현하지 않아도 사라졌습니다. 내 질문은 여기에 있습니다 :

1.) 내가 제공 한 것과 유사한 컴파일러 생성자가 컴파일러에 정의되어 있지 않습니까?

2) 복사 생성자는 실제 문제와 어떤 관련이 있습니까? 복사 생성자를 구현하면 문제가 어떻게 해결됩니까?

+0

클래스 내 애트리뷰트를 초기화하기 위해 바디 내 assigments 대신 초기화 목록을 사용하십시오. – Manu343726

+2

'push_back'의 두 번째 오버로드에서 argumment의 로컬 복사본에 대한 포인터를 저장하고 있으므로 push_back이 끝난 후에는 해당 포인터가 유효하지 않습니다. – Manu343726

답변

2
void push_A(A nA) {content->push_back(&nA); currA_valid = false;} 

여기에서 값으로 A 개체를 사용하고 있습니다. 이 객체는 함수에 대해 로컬입니다. 함수가 반환되면 객체는 더 이상 존재하지 않으므로 벡터에 잘못된 포인터가 남습니다.

복사 생성자의 구현은 문제 해결과 아무 관련이 없습니다. 단지 우연이었습니다.

+0

나는 당신이 코멘트에서 볼 수 있듯이 그것을 이미 알아 챘지만 OP의 질문에 대한 대답이 아닙니다. 그것의 코멘트. – Manu343726

+0

@ Manu343726 : 그것은 OP의 질문에 대한 대답이 아닐 수도 있지만, 그것은 그의 문제에 대한 해결책입니다. –

+0

아니요, 해결책이 아닙니다. 그 오류는 그 문제와 관련이 있습니다. 문제는 복사본에 대한 포인터를 저장하는 것이 아니라 (예, 오류입니다. 오류는 아닙니다.) 복사 생성자없이 복사본을 만드는 것입니다. 해결책은 OP를 제안하여 3 가지 규칙을 연구하는 것입니다. – Manu343726

0

예, 복사 생성자가 제공되지 않으면 컴파일러에서이를 작성합니다. 그러나 자신의 assigment 연산자 또는 소멸자를 정의하는 경우 copy-ctor의 자체 구현을 정의해야합니다.

일반적으로 해당 구문 (Copy-ctor, assigment 연산자 및/또는 소멸자) 중 하나의 구현을 작성하는 경우 다른 구현을 직접 작성해야합니다.

이것은 "The Rule Of Three"으로 알려져 있습니다.

+0

잘못되었습니다. 프로그래머 인 * us *가 따라야 할 세 가지 규칙이 있습니다. 컴파일러는 그러한 규칙을 준수하지 않습니다. 기본 구현이 잘못 정의 된 경우를 제외하고 모든 경우 (대입 연산자 또는 소멸자가 정의되었는지 여부에 관계없이)에 복사 생성자를 구현할 수 있습니다. –

+0

@BenjaminLindley 예, Rule of Three는 경험적 규칙이며 컴파일러의 규칙이 아닙니다. 그러나 thats 요점 아닙니다. 핵심은 왜 코드가 오류를 생성 하는지를 배우는 것이며,이를 해결하는 규칙입니다. – Manu343726

+0

* "그러나 자신의 assigment 연산자 또는 소멸자를 정의하면 컴파일러에서 복사 생성자의 구현을 제공 할 수 없습니다."* - 그게 중요하고 잘못되었습니다. –

관련 문제