C ++에서 생성자 및 = 연산자 오버로드 복사 : 공통 함수가 가능합니까?
복사 생성자 이후
MyClass(const MyClass&);
및 = 연산자 오버로드
MyClass& operator = (const MyClass&);
거의 동일한 코드, 동일한 매개 변수, 반환시에만 차이가있는 경우 둘 다 사용할 수있는 공통 기능을 가질 수 있습니까?
예. 두 가지 일반적인 옵션이 있습니다. 일반적으로 권장되지 않는 하나 operator=
는 복사 생성자에서를 명시 적으로 호출하는 것 입니다.
MyClass(const MyClass& other)
{
operator=(other);
}
그러나 재화를 제공하는 것은 operator=
이전 상태와 자기 할당으로 인해 발생하는 문제를 처리 할 때 도전입니다. 또한 모든 멤버와베이스는에서 할당 되더라도 먼저 기본값이 초기화됩니다 other
. 이것은 모든 구성원과 기반에 대해 유효하지 않을 수 있으며 유효한 경우에도 의미 상 중복되고 실질적으로 비용이 많이들 수 있습니다.
점점 더 많이 사용되는 솔루션은 operator=
복사 생성자와 스왑 메서드를 사용하여 구현 하는 것입니다.
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
또는:
MyClass& operator=(MyClass other)
{
swap(other);
return *this;
}
swap
단지 내부의 소유권을 바꿉니다 기존 상태를 정리하거나 새로운 자원을 할당 할 필요가 없기 때문에 기능은 쓰기에 일반적으로 간단합니다.
복사 및 스왑 관용구의 장점은 자동 할당이 안전하고 스왑 작업이 던지지 않는 경우에도 예외적으로 안전하다는 것입니다.
강력한 예외 안전을 위해 '수작업'으로 작성된 할당 연산자는 일반적으로 할당 자의 이전 리소스를 할당 해제하기 전에 새 리소스의 복사본을 할당해야하므로 새 리소스를 할당하는 동안 예외가 발생하더라도 이전 상태가 계속 반환 될 수 있습니다. . 이 모든 것은 복사 및 교체와 함께 무료로 제공되지만 일반적으로 더 복잡하므로 처음부터 오류가 발생하기 쉽습니다.
주의해야 할 한 가지는 스왑 메서드가 std::swap
복사 생성자와 할당 연산자 자체를 사용하는 기본값 이 아닌 진정한 스왑인지 확인하는 것입니다.
일반적으로 멤버 별 swap
이 사용됩니다. std::swap
작동하며 모든 기본 유형 및 포인터 유형으로 '투척 금지'가 보장됩니다. 대부분의 스마트 포인터는 던지지 않음 보장으로 교체 할 수도 있습니다.
복사 생성자는 원시 메모리였던 객체를 처음으로 초기화합니다. 할당 연산자 OTOH는 기존 값을 새 값으로 재정의합니다. 일반적으로 여기에는 오래된 리소스 (예 : 메모리)를 해제하고 새 리소스를 할당하는 작업이 포함됩니다.
둘 사이에 유사점이 있다면 할당 연산자가 파괴와 복사 생성을 수행한다는 것입니다. 일부 개발자는 배치 복사 구성에 이은 내부 파괴에 의해 실제로 할당을 구현했습니다. 그러나 이것은 매우 나쁜 생각입니다. (파생 클래스를 할당하는 동안 호출 된 기본 클래스의 할당 연산자 인 경우 어떻게됩니까?)
오늘날 일반적으로 표준 관용구로 간주되는 swap
것은 Charles가 제안한대로 사용 하고 있습니다.
MyClass& operator=(MyClass other)
{
swap(other);
return *this;
}
이것은 복사 생성 ( other
복사 된 것에 주의 )과 파괴 (함수 끝에서 파괴됨)를 사용하며, 또한 올바른 순서로 사용합니다 : 파괴 전 생성 (실패하지 않아야 함).
뭔가 신경 쓰이는 부분 :
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
첫째, 내 마음이 "복사"를 생각할 때 "스왑"이라는 단어를 읽는 것은 내 상식을 자극합니다. 또한이 멋진 트릭의 목표에 의문을 제기합니다. 예, 새 (복사 된) 리소스를 구성하는 모든 예외는 스왑 전에 발생해야합니다. 이는 모든 새 데이터가 활성화되기 전에 채워 졌는지 확인하는 안전한 방법처럼 보입니다.
That's fine. So, what about exceptions that happen after the swap? (when the old resources are destructed when the temporary object goes out of scope) From the perspective of the user of the assignment, the operation has failed, except it didn't. It has a huge side effect: the copy did actually happen. It was only some resource cleanup that failed. The state of the destination object has been altered even though the operation seems from the outside to have failed.
So, I propose instead of "swap" to do a more natural "transfer":
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
There's still the construction of the temporary object, but the next immediate action is to free all current resources of the destination before moving (and NULLing so they won't be double-freed) the resources of the source to it.
Instead of { construct, move, destruct }, I propose { construct, destruct, move }. The move, which is the most dangerous action, is the one taken last after everything else has been settled.
Yes, destruction fail is a problem in either scheme. The data is either corrupted (copied when you didn't think it was) or lost (freed when you didn't think it was). Lost is better than corrupted. No data is better than bad data.
Transfer instead of swap. That's my suggestion anyway.
'Program Tip' 카테고리의 다른 글
Unity를 사용하여 ASP.NET Web API 컨트롤러에 종속성을 삽입 할 수 없음 (0) | 2020.10.05 |
---|---|
System.currentTimeMillis ()는 UTC 시간을 반환합니까? (0) | 2020.10.05 |
P 요소 안에 중첩 될 수있는 HTML5 요소 목록? (0) | 2020.10.05 |
sqlalchemy의 선언적 ORM 확장을 사용하는 경우 다중 열 인덱스 (0) | 2020.10.05 |
윤년 버그로부터 보호하기 위해 설계된 코딩 관행을 어떻게 개발할 수 있습니까? (0) | 2020.10.05 |