2009-10-15 6 views
37

의 간단한 예를 살펴 보자 :왜 = 연산자는 정의되지 않은 구조체에서 작동합니까?

struct some_struct { 
    std::string str; 
    int a, b, c; 
} 

some_struct abc, abc_copy; 
abc.str = "some text"; 
abc.a = 1; 
abc.b = 2; 
abc.c = 3; 

abc_copy = abc; 

을 다음 abc_copy는 정확한 사본 abc입니다 .. 어떻게 = 연산자를 정의없이 가능하다? 당신이 명시 적으로 자신을 정의하지 않는 경우에 당신을 위해 일부 구성원을 합성하는 것이다

답변

67

당신은 당신을 위해 그들을 생성합니다 컴파일러 (C++ 11에 여섯)이 네 가지 방법을 정의하지 않으면 :

  • 기본 생성자
  • 복사 생성자
  • 할당 연산자
  • 소멸자
  • 이동 생성자 (C++ 11)
  • 이동 지정 (C++ 11)

이유를 알고 싶다면?
C와의 하위 호환성을 유지합니다 (C 구조체는 = 및을 사용하여 복사 가능하므로). 그러나 이것은 또한 간단한 클래스를 작성하는 것을 더 쉽게 만듭니다. 일부는 "얕은 복사 문제"때문에 문제를 추가한다고 주장합니다. 내 주장은 그것에 소유 된 RAW 포인터가있는 클래스를 가져서는 안된다는 것입니다. 적절한 스마트 포인터를 사용하면 문제가 사라집니다.

기본 생성자 (다른 생성자가 정의되지 않은 경우)

(그들이 선언 된 순서대로) 기본 클래스의 기본 생성자 및 각 구성원의 기본 생성자를 호출합니다 컴파일러 생성 된 기본 생성자 (더 소멸자 정의되지 않은 경우)

소멸자

선언의 역순으로 각 부재의 소멸을 호출한다. 그런 다음 기본 클래스의 소멸자를 호출합니다. (더 복사 생성자가 정의되지 않은 경우)

는 복사 생성자

는 SRC 객체를 전달 기본 클래스의 복사 생성자를 호출합니다. 그런 다음 src 객체 멤버를 복사 할 값으로 사용하여 각 멤버의 복사본 생성자를 호출합니다.

할당 연산자

는 SRC 객체를 전달 기본 클래스 할당 연산자를 호출합니다. 그런 다음 src 객체를 복사 할 값으로 사용하여 각 멤버에서 대입 연산자를 호출합니다.

이동 생성자 (더 이동 생성자가 정의되지 않은 경우)

는 SRC 객체를 전달 기본 클래스 이동 생성자를 호출합니다. 그런 다음 src 객체 멤버를 이동할 값으로 사용하여 각 멤버의 move 생성자를 호출합니다.

이동 할당 연산자

는 SRC 객체를 전달 기본 클래스 이동 할당 연산자를 호출합니다. 그런 다음 src 객체를 복사 할 값으로 사용하여 각 멤버의 이동 대입 연산자를 호출합니다.

이 같은 클래스를 정의하는 경우 :

struct some_struct: public some_base 
{ 
    std::string str1; 
    int a; 
    float b; 
    char* c; 
    std::string str2; 

    // Conceptually two different versions of the default constructor are built 
    // One is for value-initialization the other for zero-initialization 
    // The one used depends on how the object is declared. 
    //  some_struct* a = new some_struct;  // value-initialized 
    //  some_struct* b = new some_struct(); // zero-initialized 
    //  some_struct c;      // value-initialized 
    //  some_struct d = some_struct();  // zero-initialized 
    // Note: Just because there are conceptually two constructors does not mean 
    //  there are actually two built. 

    // value-initialize version 
    some_struct() 
     : some_base()   // value-initialize base (if compiler generated) 
     , str1()     // has a normal constructor so just call it 
     // PODS not initialized 
     , str2() 
    {} 

    // zero-initialize version 
    some_struct() 
     : some_base()   // zero-initialize base (if compiler generated) 
     , str1()     // has a normal constructor so just call it. 
     , a(0) 
     , b(0) 
     , c(0) // 0 is NULL 
     , str2() 
     // Initialize all padding to zero 
    {} 

    some_struct(some_struct const& copy) 
     : some_base(copy) 
     , str1(copy.str1) 
     , a(copy.a) 
     , b(copy.b) 
     , c(copy.c) 
     , str2(copy.str2) 
    {} 

    some_struct& operator=(some_struct const& copy) 
    { 
     some_base::operator=(copy); 
     str1 = copy.str1; 
     a = copy.a; 
     b = copy.b; 
     c = copy.c; 
     str2 = copy.str2; 
     return *this; 
    } 

    ~some_struct() 
    {} 
    // Note the below is pseudo code 
    // Also note member destruction happens after user code. 
    // In the compiler generated version the user code is empty 
     : ~str2() 
     // PODs don't have destructor 
     , ~str1() 
     , ~some_base(); 
    // End of destructor here. 

    // In C++11 we also have Move constructor and move assignment. 
    some_struct(some_struct&& copy) 
        // ^^^^ Notice the double && 
     : some_base(std::move(copy)) 
     , str1(std::move(copy.str1)) 
     , a(std::move(copy.a)) 
     , b(std::move(copy.b)) 
     , c(std::move(copy.c)) 
     , str2(std::move(copy.str2)) 
    {} 

    some_struct& operator=(some_struct&& copy) 
           // ^^^^ Notice the double && 
    { 
     some_base::operator=(std::move(copy)); 
     str1 = std::move(copy.str1); 
     a = std::move(copy.a); 
     b = std::move(copy.b); 
     c = std::move(copy.c); 
     str2 = std::move(copy.str2); 
     return *this; 
    } 
}; 
+0

이것은 아주 훌륭하게 좋은 대답이지만 이미 스마트 포인터를 사용한 예를보고 싶습니다. 나는 결코 auto_ptr에서 놀랐다. – Hamy

+0

@Hamy : 이것은 스마트 포인터를 만드는 데 필요한 정보이다. 똑똑한 포인터를 사용한다면 실제로 걱정할 필요가 없습니다. 클래스에 RAW 소유 포인터가있는 경우 위의 내용 만 신경 써야합니다. –

+1

이 답변은 [초기화] (http://en.cppreference.com/w/cpp/language/initialization) 유형을 혼란스럽게합니다. 이니셜 라이저가 없으면 struct는 [* default initialized *] (http://en.cppreference.com/w/cpp/language/default_initialization)가됩니다. POD 유형 멤버는 불확정 값을 가정합니다. 비어있는 초기화자를 사용하면 구조체는 [* value initialized *] (http://en.cppreference.com/w/cpp/language/value_initialization) 될 것입니다. POD 유형 멤버는 [* zero initialized *] (http : //en.cppreference.com/w/cpp/language/zero_initialization). –

2

컴파일러를

는 (.. 일부 코드 작업을 할 때 나를 놀라게했다). 대입 연산자가 그 중 하나입니다. 복사 생성자는 또 하나이며 소멸자도 있습니다. 당신이 생성자를 제공하지 않는다면 기본 생성자를 얻는다. 저 이외에 나는 다른 무엇이 있을지 모르지만 나는 다른 사람들이있을 수 있다고 믿는다. (280Z28에 의해 제공된 답안의 링크는 그렇지 않다는 것을 암시하며 나는 그것을 읽었던 곳을 기억하지 못한다.

0

구조체는 기본적으로 메모리에있는 해당 구성 요소의 연결입니다 (정렬을 위해 내장 된 일부 패딩이 있음). 하나의 구조체에 다른 구조체의 값을 할당하면 그 값은 그냥 넘어갑니다.

4

그러나 정의되어 있습니다. 표준에서. operator = 연산자를 제공하지 않으면, operator =가 제공됩니다. 그리고 기본 연산자는 각 멤버 변수를 복사합니다. 그리고 각 회원을 복사하는 방법을 어떻게 알 수 있습니까? 연산자 =를 호출합니다 (정의되지 않은 경우 기본적으로 ...가 제공됩니다).

4

그 동작은 구조체는 일반적 = 연산자로 복사되도록

C, 당신에게/재정의 연산자를 정의 할 수있는 기능을 제공하지 않습니다 C.

와 소스 호환성을 유지하기 위해 필요하다.

+0

K & R C는'='에서 함께 복사 할 구조를 허용하지 않았다 구축 할 것입니다 컴파일러는 무엇

struct some_struct: public some_base { std::string str1; int a; float b; char* c; std::string str2; }; 

모두, 나는 C89에 대해 확신하지 못한다. 그것이 C99에서 소개 되었다면, 나는 그것이 C++ 영향으로 인한 것이라고 주장 할 것입니다. – ephemient

+1

K & R (2nd edition, 1988, p. 127)에 따르면 ANSI C에 의해 소개되었지만 대부분의 기존 컴파일러가 이미 지원했습니다. – Ferruccio

3

할당 연산자 (operator=)은 C++의 구조체 또는 클래스에 대해 암시 적으로 생성 된 함수 중 하나입니다. 요컨대
http://www.cs.ucf.edu/~leavens/larchc++manual/lcpp_136.html

이 내재적 생성 부재는 memberwise shallow copy을 수행

여기에 4 내재적 생성 부재를 설명하는 참조이다. 다음은 링크 된 페이지의 긴 버전입니다.

필요할 때 암시 적으로 생성 된 할당 연산자 지정자는 다음과 같습니다. 스펙은 결과가 할당되는 오브젝트 (self)이며, 이후 상태 selfself의 추상 값의 값 것을 "인수 from의 추상 값의 값과 동일한 것을 말한다.

C++에서
// @(#)$Id: default_assignment_op.lh,v 1.3 1998/08/27 22:42:13 leavens Exp $ 
#include "default_interfaces.lh" 

T& T::operator = (const T& from) throw(); 
//@ behavior { 
//@ requires assigned(from, any) /\ assigned(from\any, any); 
//@ modifies self; 
//@ ensures result = self /\ self" = from\any\any; 
//@ ensures redundantly assigned(self, post) /\ assigned(self', post); 
//   thus 
//@ ensures redundantly assigned(result, post) /\ assigned(result', post); 
//@ } 
+1

빈 예외 사양이 맞습니까? –

+0

기본 할당 연산자는 메모리를 할당하지 않기 때문에 던질 수 없습니다. : dunno : –

+3

@Rob : 12.8 : 10에서 시작하는 기본 복사 할당 연산자의 정의는 throw 절의 언급을하지 않습니다. 기본 복사 할당 연산자가 기본값 이외의 할당을 호출 할 수 있기 때문에 이는 나에게 의미가 있습니다. 분명히 주어진 예제에서'std :: string :: operator = (const std :: string &)'를 던질 수 있습니다. –

7

, 구조체 오히려 개인 접근보다는 대중이 경우 회원의 기본 클래스에 해당합니다

가 제공되지 않는 경우 자동 또한 클래스의 다음과 같은 특수 멤버를 생성합니다 C++ 컴파일러 :.

  • 기본 생성자 - 인수가 없으면 기본값이 모든 항목을 초기화합니다.
  • 복사 생성자 - 동일한 클래스의 다른 객체에 대한 참조를 취하는 클래스와 이름이 같은 메소드입니다. 모든 값을 복사합니다.
  • 소멸자 - 객체가 파괴 될 때 호출됩니다. 기본적으로 아무 작업도 수행하지 않습니다.
  • 할당 연산자 - 하나의 구조체/클래스가 다른 구조체/클래스에 할당되면 호출됩니다. 위의 경우에 호출되는 자동 생성 된 메서드입니다.
+1

* 사용자 정의 생성자가 *있는 경우 암시 적 기본 생성자도 제공되지 않습니다. – sellibitze

+0

암시 적 소멸자가 구성원 및 하위 객체의 소멸자도 호출합니다 (있는 경우) – sellibitze