3의 법칙은 무엇입니까?


2146
  • 객체 복사 란 무엇입니까 ?
  • 무엇 복사 생성자복사 대입 연산자는 ?
  • 언제 스스로 신고해야합니까?
  • 객체가 복사되지 않도록하려면 어떻게해야합니까?

52
제발 읽어 이 전체 스레드태그 위키를 닫으 투표를하기 전에c++-faq .
sbi

13
@Binary : 투표를 하기 전에 댓글 토론을 읽는 데 적어도 시간이 걸립니다 . 텍스트는 훨씬 단순 해졌지만 Fred는 그것을 확장하라는 요청을 받았습니다. 또한 그것은 문법적으로 네 가지 질문 이지만 실제로는 여러 측면을 가진 하나의 질문입니다. (동의하지 않을 경우 각 질문에 스스로 대답하여 POV를 입증하고 결과에 투표하도록하겠습니다.)
sbi

1
프레드, 여기에 C ++ 1x에 관한 흥미로운 답변이 있습니다 : stackoverflow.com/questions/4782757/… . 우리는 이것을 어떻게 처리합니까?
sbi


4
C ++ 11부터는 이것이 5의 규칙 또는 이와 유사한 것으로 업그레이드되었다고 생각하십시오.
paxdiablo

답변:


1794

소개

C ++는 사용자 정의 유형의 변수를 값 의미론으로 처리 합니다. 이는 객체가 다양한 컨텍스트에서 암시 적으로 복사됨을 의미하며, "객체 복사"가 실제로 무엇을 의미하는지 이해해야합니다.

간단한 예를 살펴 보겠습니다.

class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

( name(name), age(age)부품에 의아해하는 경우 이를 멤버 이니셜 라이저 목록 이라고 합니다 .)

특별 회원 기능

person객체 를 복사한다는 것은 무엇을 의미 합니까? 이 main기능은 두 가지 별개의 복사 시나리오를 보여줍니다. 초기화 person b(a);복사 생성자에 의해 수행됩니다 . 기존 객체의 상태에 따라 새로운 객체를 구성하는 것이 그 임무입니다. 할당 b = a복사 할당 연산자에 의해 수행됩니다 . 대상 객체가 이미 처리해야 할 유효한 상태에 있기 때문에 작업이 일반적으로 조금 더 복잡합니다.

우리는 복사 생성 자나 할당 연산자 (또는 소멸자)를 스스로 선언하지 않았기 때문에 암시 적으로 정의되어 있습니다. 표준에서 인용 :

[...] 복사 생성자와 복사 할당 연산자, [...] 및 소멸자는 특수 멤버 함수입니다. [ 참고 : 구현시 프로그램에서 명시 적으로 선언하지 않은 경우 일부 클래스 유형에 대해 이러한 멤버 함수를 암시 적으로 선언합니다. 구현은 사용되는 경우 암시 적으로 정의합니다. [...] 끝 참고 ] [n3126.pdf section 12 §1]

기본적으로 객체를 복사한다는 것은 해당 멤버를 복사하는 것을 의미합니다.

비 유니언 클래스 X에 대한 암시 적으로 정의 된 복사 생성자는 해당 서브 오브젝트의 멤버 별 사본을 수행합니다. [n3126.pdf 섹션 12.8 §16]

비 유니언 클래스 X에 대해 암시 적으로 정의 된 복사 할당 연산자는 해당 서브 오브젝트의 멤버 별 복사 할당을 수행합니다. [n3126.pdf 섹션 12.8 §30]

암시 적 정의

암시 적으로 정의 된 특수 멤버 함수는 person다음과 같습니다.

// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

: Memberwise 복사는 우리가이 경우에 원하는 것을 정확하게 name그리고 age우리가 자체 포함, 독립적 얻을 수 있도록, 복사 person개체를. 암시 적으로 정의 된 소멸자는 항상 비어 있습니다. 이 경우에도 생성자에서 리소스를 얻지 못했기 때문에 좋습니다. 멤버의 소멸자는 person소멸자가 완료된 후에 암시 적으로 호출됩니다 .

소멸자의 본문을 실행하고 본문 내에 할당 된 자동 객체를 삭제 한 후, 클래스 X의 소멸자는 X의 직접 [...] 멤버의 소멸자를 호출합니다. [n3126.pdf 12.4 §6]

자원 관리

그렇다면 언제 특수 멤버 함수를 명시 적으로 선언해야합니까? 클래스 가 리소스를 관리 할 때 , 즉 클래스의 객체가 해당 리소스를 담당 할 때 . 이는 일반적으로 생성자에서 리소스를 거나 생성자에 전달 하여 소멸자에서 해제 한다는 것을 의미합니다 .

사전 표준 C ++로 시간을 거슬러 갑시다. 와 같은 것은 없었 std::string으며 프로그래머는 포인터를 좋아했습니다. person클래스는이처럼 보였다 수도 :

class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

오늘날에도 여전히 사람들이 쓰기 수업이 스타일과 곤경에 얻을는 " 나는 벡터에 사람을 밀어 지금은 미친 메모리 오류를 얻을! "기본적으로 회원을 복사하는 개체 수단을 복사하지만 복사 기억 name단지 회원 가리키는 문자 배열이 아닌 포인터를 복사합니다 ! 이것은 몇 가지 불쾌한 효과가 있습니다.

  1. 를 통한 변경 사항은를 통해 a확인할 수 있습니다 b.
  2. 일단 b파괴 되면 a.name매달려있는 포인터입니다.
  3. a삭제되면 매달려 포인터를 삭제하면 정의되지 않은 동작이 생성 됩니다.
  4. 과제는 과제 name이전에 지적한 사항을 고려하지 않기 때문에 조만간 메모리 누수가 발생합니다.

명시 적 정의

멤버 별 복사는 원하는 효과가 없으므로 문자 배열의 깊은 복사본을 만들려면 복사 생성자와 복사 할당 연산자를 명시 적으로 정의해야합니다.

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

초기화와 할당의 차이점에 유의하십시오 name. 메모리 누수를 방지하려면 할당하기 전에 이전 상태를 해제해야합니다 . 또한 양식의 자체 할당으로부터 보호해야합니다 x = x. 그 검사없이 delete[] name포함하는 배열 삭제 것입니다 소스 당신이 쓸 때 때문에, 문자열 x = x, 모두 this->namethat.name같은 포인터를 포함합니다.

예외 안전

불행히도 new char[...]메모리 소진으로 인해 예외 가 발생하면이 솔루션이 실패합니다 . 가능한 해결책 중 하나는 지역 변수를 도입하고 명령문을 재정렬하는 것입니다.

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

또한 명시적인 확인없이 자체 할당을 처리합니다. 이 문제에 대한 더욱 강력한 해결책은 copy-and-swap idiom 이지만 예외 안전에 대한 자세한 내용은 다루지 않겠습니다. 나는 다음과 같은 점을 지적하기 위해 예외를 언급 했다. 자원을 관리하는 클래스를 작성하는 것은 어렵다.

복사 할 수없는 리소스

파일 핸들 또는 뮤텍스와 같은 일부 리소스는 복사 할 수 없거나 복사해서는 안됩니다. 이 경우 간단히 private정의를 제공하지 않고 복사 생성자와 복사 할당 연산자를 선언하십시오 .

private:

    person(const person& that);
    person& operator=(const person& that);

또는 boost::noncopyableC ++ 11 이상에서 삭제 된 것으로 상속 하거나 선언 할 수 있습니다 .

person(const person& that) = delete;
person& operator=(const person& that) = delete;

세의 규칙

때때로 자원을 관리하는 클래스를 구현해야합니다. (단일 클래스에서 여러 리소스를 관리하지 않으면 고통을 초래할뿐입니다.)이 경우 세 가지 규칙을 기억하십시오 .

소멸자, 복사 생성자 또는 복사 할당 연산자를 명시 적으로 선언해야하는 경우 세 가지를 모두 명시 적으로 선언해야합니다.

(안타깝게도이 "규칙"은 C ++ 표준 또는 내가 알고있는 컴파일러에 의해 시행되지 않습니다.)

5의 규칙

C ++ 11부터 객체에는 이동 생성자와 이동 할당이라는 두 가지 추가 특수 멤버 함수가 있습니다. 이러한 기능을 구현하는 5 가지 상태의 규칙

서명이있는 예 :

class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // Copy Ctor
    person(person &&) noexcept = default;            // Move Ctor
    person& operator=(const person &) = default;     // Copy Assignment
    person& operator=(person &&) noexcept = default; // Move Assignment
    ~person() noexcept = default;                    // Dtor
};

제로의 규칙

3/5의 규칙은 0/3/5의 규칙이라고도합니다. 규칙의 0 부분은 클래스를 만들 때 특수 멤버 함수를 작성할 수 없음을 나타냅니다.

조언

대부분의 경우 std::string이미 리소스를 이미 관리하고 있기 때문에 리소스를 직접 관리 할 필요가 없습니다 . std::string멤버를 사용하는 간단한 코드를 a 를 사용하여 복잡하고 오류가 발생하기 쉬운 대안 char*과 비교하면 확신 할 수 있습니다. 원시 포인터 멤버에서 멀리 떨어져있는 한 세 규칙은 자신의 코드와 관련이 없습니다.


4
프레드, 나는 (A) 당신이 복사 가능한 코드로 잘못 구현 된 과제를 철자하지 않고 그것이 잘못되었다는 메모를 추가하고 글씨의 다른 부분을 보지 않는다면, 나의 투표에 대해 더 기분이 좋을 것이다. 코드에서 c & s를 사용하거나 이러한 모든 멤버 구현을 건너 뛰십시오 (B) 상반기를 단축하면 RoT와 거의 관련이 없습니다. (C) 당신은 이동 의미론의 도입과 그것이 RoT에 어떤 의미가 있는지 논의 할 것이다.
sbi

7
그러나 포스트는 C / W로 만들어야한다고 생각합니다. 용어를 대부분 정확하게 유지하는 것이 좋습니다 (즉, " 복사 할당 연산자" 라고 말하고 할당이 복사를 암시 할 수없는 공통 트랩을 사용하지 않는 것).
Johannes Schaub-litb

4
@Prasoon : 나는 답변의 절반을 잘라내는 것이 비 CW 답변의 "공정한 편집"으로 보일 것이라고 생각하지 않습니다.
sbi

69
C ++ 11에 대한 게시물을 업데이트하면 (즉, 생성자 / 할당 이동)
Alexander Malakhov

5
@solalito 사용 후 해제해야 할 사항 : 동시성 잠금, 파일 핸들, 데이터베이스 연결, 네트워크 소켓, 힙 메모리 ...
fredoverflow

509

세 가지의 규칙은 C ++에 대한 엄지 손가락의 규칙입니다, 기본적으로 말

수업이 필요한 경우

  • 복사 생성자 ,
  • 할당 연산자 ,
  • 또는 소멸자 ,

명확하게 정의 하면 세 가지모두 필요할 입니다.

그 이유는 세 가지가 모두 리소스를 관리하는 데 일반적으로 사용되기 때문에 클래스가 리소스를 관리하는 경우 일반적으로 복사 및 해제를 관리해야합니다.

클래스가 관리하는 리소스를 복사하는 데 의미가 없으면 복사 생성자와 할당 연산자를로 선언하여 ( 정의 하지 않음 ) 복사를 금지하는 것이 private좋습니다.

(다가오는 C ++ 표준의 새로운 버전 (C ++ 11)은 C ++에 이동 의미를 추가하여 3의 규칙을 변경 할 수 있습니다. 그러나 C ++ 11 섹션을 작성하기에는 이것에 대해 거의 알지 못합니다. 3의 법칙에 대해.)


3
복사를 방지하는 또 다른 솔루션은 (사설처럼 boost::noncopyable) 복사 할 수없는 클래스에서 (개인적으로) 상속하는 것 입니다. 또한 훨씬 더 명확 할 수 있습니다. C ++ 0x와 "삭제"기능이 여기에 도움이 될 수 있지만 다음과 같은 구문을 잊어 버렸습니다. /
Matthieu M.

2
@ Matthieu : 그렇습니다. 그러나 noncopyable표준 라이브러리의 일부가 아닌 한 , 나는 그것을 많이 개선하지는 않습니다. (아, 그리고 당신이 삭제 구문을 잊어 버린 경우, 당신은 내가 모르는 mor ethan을 잊었다. :))
sbi

3
@Daan : 이 답변을 참조하십시오 . 그러나 MartinhoRule of Zero 를 고수하는 것이 좋습니다 . 나에게 이것은 지난 10 년 동안 만들어진 C ++의 가장 중요한 규칙 중 하나입니다.
sbi 2016 년

3
에있는 지금은 더 나은 (분명 애드웨어 인수없이) 제로의 마르틴의 규칙 archive.org
네이 키드

161

빅 3의 법칙은 위에서 지정한대로입니다.

일반 영어로 해결되는 문제의 쉬운 예 :

기본이 아닌 소멸자

생성자에 메모리를 할당 했으므로 소멸자를 작성하여 삭제해야합니다. 그렇지 않으면 메모리 누수가 발생합니다.

이 작업이 완료된 것으로 생각할 수 있습니다.

문제는 사본이 객체로 만들어진 경우 사본이 원래 객체와 동일한 메모리를 가리킬 것입니다.

일단 이것들 중 하나가 소멸자에서 메모리를 삭제하면 다른 하나는 유효하지 않은 메모리에 대한 포인터를 갖게됩니다 (이것은 매달려있는 포인터라고 불립니다).

따라서 복사 할 생성자를 작성하여 새 객체에 자체 메모리 조각을 할당하여 파기합니다.

할당 연산자 및 복사 생성자

생성자의 메모리를 클래스의 멤버 포인터에 할당했습니다. 이 클래스의 객체를 복사하면 기본 할당 연산자와 복사 생성자가이 멤버 포인터의 값을 새 객체에 복사합니다.

즉, 새 개체와 이전 개체는 같은 메모리 조각을 가리 키므로 한 개체에서 변경하면 다른 개체도 변경됩니다. 한 개체가이 메모리를 삭제하면 다른 개체는이 메모리를 계속 사용하려고합니다.

이 문제를 해결하려면 고유 한 버전의 복사 생성자와 할당 연산자를 작성하십시오. 버전은 새 객체에 별도의 메모리를 할당하고 첫 번째 포인터가 주소가 아닌 가리키는 값을 복사합니다.


4
따라서 복사 생성자를 사용하면 복사가 이루어 지지만 다른 메모리 위치에 모두 생성되고 복사 생성자를 사용하지 않으면 복사가 이루어 지지만 동일한 메모리 위치를 가리 킵니다. 당신이 말하려는 것입니까? 따라서 복사 생성자가없는 사본은 새로운 포인터가 있지만 동일한 메모리 위치를 가리키는 것을 의미하지만 사용자가 명시 적으로 정의한 복사 생성자가있는 경우 다른 메모리 위치를 가리 키지 만 데이터가있는 별도의 포인터가 있습니다.
깨지지 않는

4
죄송합니다,이 연령대에 이전에 회신했지만 내 회신이 아직 여기에없는 것 같습니다 :-( 기본적으로, 예-알 수 있습니다 :-)
Stefan

1
원칙은 복사 할당 연산자에 어떤 영향을 미칩니 까? 이 답변은 3의 규칙에서 3이 언급되면 더 유용 할 것입니다.
DBedrenko

1
@DBedrenko, "복사 생성자를 작성하여 새로운 객체에 자체 메모리 조각을 할당하도록 ..."이것은 복사 할당 연산자로 확장되는 것과 동일한 원리입니다. 내가 분명히했다고 생각하지 않습니까?
Stefan

2
@DBedrenko, 더 많은 정보를 추가했습니다. 그것이 더 명확합니까?
Stefan

44

기본적으로 소멸자 (기본 소멸자가 아님)가 있으면 정의한 클래스에 메모리 할당이 있음을 의미합니다. 클래스가 일부 클라이언트 코드 또는 사용자가 외부에서 사용한다고 가정하십시오.

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

MyClass에 기본 형식의 멤버가 일부만 있으면 기본 할당 연산자가 작동하지만 할당 연산자가없는 포인터 멤버 및 개체가 있으면 결과를 예측할 수 없습니다. 따라서 클래스의 소멸자에서 삭제할 항목이 있으면 깊은 복사 연산자가 필요할 수 있습니다. 즉 복사 생성자와 할당 연산자를 제공해야합니다.


36

객체 복사 란 무엇입니까? 딥 카피와 얕은 카피라는 객체를 복사 할 수있는 몇 가지 방법이 있습니다.

우리는 객체 지향 언어 (또는 적어도 그렇게 가정하고 있음)이므로 메모리 조각이 할당되었다고 가정 해 봅시다. OO 언어이므로 할당 된 메모리 청크를 쉽게 참조 할 수 있습니다. 일반적으로 기본 변수 (int, char, bytes) 또는 자체 유형 및 기본 형식으로 정의한 클래스이기 때문에 할당 한 메모리 청크를 쉽게 참조 할 수 있습니다. 다음과 같이 Car 클래스가 있다고 가정 해 봅시다.

class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

딥 카피는 객체를 선언 한 다음 객체의 완전히 별개의 사본을 생성하는 것입니다. 우리는 2 개의 메모리 세트에 2 개의 객체가 생깁니다.

Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

이제 이상한 일을하자. car2가 잘못 프로그래밍되었거나 의도적으로 car1의 실제 메모리를 공유하도록되어 있다고 가정 해 봅시다. (이 작업을 수행하는 것은 일반적으로 실수이며 수업 시간에는 일반적으로 아래에서 논의되는 담요입니다.) car2에 대해 질문 할 때마다 car1의 메모리 공간에 대한 포인터를 실제로 해결하고 있다고 가정하십시오. 입니다.

//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

따라서 어떤 언어로 작성하든 관계없이 대부분의 경우 딥 카피를 원하기 때문에 객체를 복사 할 때의 의미에 유의하십시오.

복사 생성자와 복사 할당 연산자는 무엇입니까? 나는 이미 그것들을 사용했다. 복사 생성자는 Car car2 = car1; 본질적으로 변수를 선언하고 한 줄에 변수를 할당하면 복사 생성자가 호출 될 때와 같이 코드를 입력 할 때 호출됩니다. 대입 연산자는 등호-를 사용할 때 발생합니다 car2 = car1;. 통지 car2는 동일한 진술로 선언되지 않습니다. 이러한 작업을 위해 작성하는 두 코드 조각은 매우 유사합니다. 실제로 일반적인 디자인 패턴에는 초기 복사 / 할당이 만족스러운 경우 모든 것을 설정하기 위해 호출하는 또 다른 함수가 있습니다. 필자가 작성한 긴 코드를 보면 함수는 거의 동일합니다.

언제 스스로 신고해야합니까? 공유하거나 프로덕션 용 코드를 작성하지 않는 경우 필요할 때만 선언하면됩니다. '우연히'언어를 사용하기로 선택하고 언어를 만들지 않은 경우 프로그램 언어가 무엇인지 알고 있어야합니다. 즉, 컴파일러 기본값을 사용합니다. 예를 들어 복사 생성자를 거의 사용하지 않지만 할당 연산자 재정의는 매우 일반적입니다. 더하기, 빼기 등의 의미를 무시할 수 있다는 것을 알고 있습니까?

객체가 복사되지 않도록하려면 어떻게해야합니까? 개인 기능으로 객체에 메모리를 할당 할 수있는 모든 방법을 무시하는 것이 합리적 시작입니다. 사람들이 실제로 복사하는 것을 원하지 않으면 공개를 만들고 예외를 발생시키고 객체를 복사하지 않음으로써 프로그래머에게 경고 할 수 있습니다.


5
질문은 C ++로 태그되었습니다. 이 의사 코드 박람회는 잘 정의 된 "Rule Of Three"에 대한 설명을 거의 제공하지 않으며 최악의 경우 혼란을 퍼뜨립니다.
sehe

26

언제 스스로 신고해야합니까?

세 가지 규칙에 따르면

  1. 복사 생성자
  2. 복사 할당 연산자
  3. 파괴 장치

그런 다음 세 가지를 모두 선언해야합니다. 복사 작업의 의미를 인계해야 할 필요성은 거의 항상 일종의 자원 관리를 수행하는 클래스에서 비롯되었으며 거의 ​​항상 그 의미를 암시했습니다.

  • 한 복사 작업에서 수행 된 모든 자원 관리는 다른 복사 작업에서 수행되어야 할 것입니다.

  • 클래스 소멸자는 리소스 관리에 참여할 것입니다 (보통 리소스를 공개). 관리해야 할 기본 리소스는 메모리 였기 때문에 메모리를 관리하는 모든 표준 라이브러리 클래스 (예 : 동적 메모리 관리를 수행하는 STL 컨테이너)가 모두 복사 작업과 소멸자 모두 "큰 세 가지"를 선언하는 것입니다.

3의 규칙의 결과는 사용자가 선언 한 소멸자가 있다는 것은 간단한 멤버 현명한 사본이 클래스의 복사 작업에 적합하지 않을 것임을 나타냅니다. 즉, 클래스가 소멸자를 선언하면 올바른 작업을 수행하지 않기 때문에 복사 작업이 자동으로 생성되지 않아야합니다. C ++ 98이 채택되었을 때,이 추론의 중요성은 충분히 인식되지 않았으므로, C ++ 98에서 사용자 선언 소멸자가 존재하더라도 복사 작업을 생성하려는 컴파일러의 의지에는 영향을 미치지 않았습니다. C ++ 11에서는 계속 그렇습니다. 그러나 복사 작업이 생성되는 조건을 제한하면 레거시 코드가 너무 많이 손상되기 때문입니다.

객체가 복사되지 않도록하려면 어떻게해야합니까?

복사 생성자 및 복사 할당 연산자를 개인용 액세스 지정자로 선언하십시오.

class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

C ++ 11 이후에는 복사 생성자 및 할당 연산자가 삭제됨을 선언 할 수도 있습니다

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}


10

C ++에서 3의 규칙은 다음 멤버 함수 중 하나에 명확한 정의가있는 경우 프로그래머가 다른 두 멤버 함수를 함께 정의해야하는 세 가지 요구 사항의 설계 및 개발의 기본 원칙입니다. 즉, 소멸자, 복사 생성자, 복사 할당 연산자의 세 가지 멤버 함수는 필수입니다.

C ++의 복사 생성자는 특수 생성자입니다. 기존 객체의 복사본에 해당하는 새로운 객체 인 새 객체를 만드는 데 사용됩니다.

복사 할당 연산자는 일반적으로 기존 객체를 동일한 유형의 객체로 다른 객체에 지정하는 데 사용되는 특수 할당 연산자입니다.

빠른 예가 있습니다.

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

7
안녕, 당신의 대답은 새로운 것을 추가하지 않습니다. 다른 것들은 주제를 훨씬 더 깊이, 더 정확하게 다룰 것입니다-당신의 대답은 대략적이고 실제로 어떤 장소에서는 틀립니다 (즉, 여기에 "필수"는 없습니다; 그것은 "아마도해야합니다"). 이미 철저하게 답변 된 질문에 이러한 종류의 답변을 게시하는 동안 귀하의 가치가 없습니다. 새로운 것을 추가하지 않는 한.
Mat

1
또한, 거기에 되어 빠른 예, 어떻게 든 관련 세 가지 세 가지의 규칙에 대해 이야기하고 있다고는. 너무 많은 혼란.
anatolyg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.