2014-12-11 3 views
1

복사 생성자와 이동 시맨틱에 대해 수많은 기사를 읽었습니다. 나는 일종의 '일종의'일을 이해하고있는 것처럼 느낀다. 그러나 많은 설명은 실제로 두포 (내가 혼란을 일으키는 원인이되는 것)에서 실제로 일어나고있는 것을 버린다. 실제로 객체와 메모리에 무슨 일이 벌어지고다음의 객체로 생성자와 시맨틱을 복사하십시오.

string b(x + y); 

string(string&& that) 
    { 
     data = that.data; 
     that.data = 0; 
    } 

: 예를 들어

? 따라서 어떤 객체가 'b'이고 그 값은 x + y이며 rvalue는 이동 생성자를 호출합니다. 이것은 정말로 나를 혼란스럽게 만들고 있습니다 ... 왜 그렇게합니까?

데이터를 복사하는 대신 '이동'하는 것이 좋다고 생각합니다.하지만 여기서 잃어버린 부분은 메모리 수준에서 각 객체/매개 변수에 어떤 일이 일어나는지를 함께 분석하려고 할 때입니다.

죄송합니다. 혼란 스러울 경우 이야기를해도 혼란 스럽습니다.

편집 :

가 요약하면, 나는 복사 생성자와 이동 생성자의 ... 난 그냥 이해하지 못하는 '이유'는 '어떻게'이해합니다.

+1

메모리에 :'this.data'의 값을'this-> data'에 복사하고'that.data'를'0'으로 설정하십시오. "뒷이야기"가 없습니다. –

+0

이것이 도움이 될만한 IDK이지만 * copy *는 다음과 같을 수 있습니다 :'char * b = new char [strlen (a) +1]; strcpy (b, a); 삭제 [] a; a = nullptr;'이고, * move *는'char * b = a; a = nullptr;'. 분명히 후자는 할당이 적으므로 그러한 의미를 원할 때 선호됩니다. –

답변

1
... (x + y); 

짧은 문자열 최적화가 작동하지 않는다고 가정 해 봅시다. 문자열 구현이 그것을 사용하지 않거나 문자열 값이 너무 길기 때문입니다."전에"- 값을 기준으로 operator+ 반환, 그래서 ... xy 문자열에 전혀 관련이없는 새로운 버퍼

[ string { const char* _p_data; ... } ] 
          \ 
          \-------------------------(heap)--------[ "hello world!" ]; 

산세 최적화를 임시을 만들 수 있으며, 그는 string 생성자의 인수를 준비 끝났다 그 생성자가 논쟁으로 무엇을 할 것인지를 생각해보십시오. 컴파일러는 임시 위에서 이동에 적합 함을 이해할 것처럼

string b(x + y); 

여기에 string(string&&) 생성자가 호출된다.

[ string { const char* _p_data; ... } ] 
          \ 
          \-------------------------(heap)--------[ "hello world!" ]; 


[ string b { const char* _p_data; ... } ] 
          \ 
          \----? uninitialised 

b의 이동 생성자는 다음 임시에서 기존 힙 버퍼를 도용입니다 대상 : 문맥 다시 표시 임시로 아래 그림과 같은 것을 - 생성자는 텍스트의 포인터가 초기화되지 않은되고, 실행을 시작하면 .

      nullptr 
         /
[ string { const char* _p_data; ... } ] 

          -------------------------(heap)--------[ "hello world!" ]; 
          /
         /
[ string b { const char* _p_data; ... } ] 

는 또한의 임시 소멸자를 실행하면 지금 b 소유로 간주되지 delete[] 버퍼를하지 있는지 확인하기 위해 임시의 _p_datanullptr로 설정해야합니다. (이동 생성자는 다른 데이터 멤버도 "이동"합니다. "용량"값, "끝"위치 또는 "크기"값에 대한 포인터 등).

이 모든 것은 b의 생성자가 두 번째 힙 버퍼를 만들고 모든 텍스트를 복사 한 다음 임시 버퍼를 delete[]으로 추가 작업하는 것을 방지합니다.

+0

Brilliant. 고맙습니다. – Kris

0

(x + y)은 문자열 값을 제공합니다. 복사하지 않고 b에 저장하려고합니다. 이것은 Return Value Optimization (RVO)에 의해 C++ 11과 이동 시멘틱스 이전에 가능하게되었습니다.

+0

'그'는 'x + y'를 가리키며 그 값은 데이터를 복사하지 않고 'b'객체를 만드는 데 사용됩니까? x + y 값은 어떻게됩니까? 포인터는 어떻게 복사됩니까? rvalue는 마지막 곱슬에서 사라지는 임시 객체입니까? – Kris

+0

또한 이러한 최적화는 C++ 98에서 보장되지 않는다는 점에 유의해야합니다. – Kris

2

복잡한 개체는 일반적으로 완전히 스택 기반이 아닙니다. 예제 개체를 예로 들어 봅시다.

class String { 
public: 
    // happy fun API 
private: 
    size_t size; 
    char* data; 
}; 

대부분의 문자열과 마찬가지로 문자열은 문자 배열입니다. 그것은 본질적으로 문자 배열과 적절한 크기를 유지하는 객체입니다.

사본의 경우 두 단계가 있습니다. 먼저 size을 복사 한 다음 data을 복사하십시오. 그러나 data은 단지 포인터 일뿐입니다. 따라서 객체를 복사 한 다음 원본을 수정하면 두 위치가 동일한 데이터를 가리키고 복사본이 변경됩니다. 이것은 우리가 원하는 것이 아닙니다.

따라서해야 할 일은 먼저 객체를 만들 때 수행했던 것과 동일한 작업을 수행하는 것입니다. newdata을 적당한 크기로 만듭니다.

: 우리는 단지 데이터를 이동해야하는 경우
String::String(String const& copy) { 
    size = copy.size; 
    data = new int[size]; 
    memcpy(data, copy.data, size); 
} 

그러나 다른 한편으로

은, 우리가 뭔가를 할 수 있습니다 : 우리는 객체를 복사 할 때

그래서 우리는 같은 것을 할 필요가

String::String(String&& copy) { 
    size = copy.size; 
    data = copy.data; 
    copy.size = 0; 
    copy.data = nullptr; // So copy's dtor doesn't try to free our data. 
} 

이제 막후에서 포인터는 단지 우리에게 전달되었습니다. 더 이상 정보를 할당 할 필요가 없었습니다. 이것이 움직임이 선호되는 이유입니다. 그 메모리를 가져올 수있다, 그래서 할당과 힙에 메모리를 복사하는 것은 그것이 스택에 로컬 일이 아니기 때문에 매우 비용이 많이 드는 작업 할 수 있습니다, 그것은 다른 곳에서 무슨 일이 일어나고, 그것은 등, 캐시되지 않을 수도 있습니다

+0

이것은 완벽했습니다. 나는 지금 그것을 완전히 얻는다. .. 시간을내어 그것을 놓아 줘서 고마워. 그 개념은 나에게 새로운 것이었다. – Kris