C++에선 클래스가 리소스를 관리하고 클래스의 생성/소멸에 맞춰 리소스를 할당/해제한다. 관련된 멤버 함수와 그 역할은 아래와 같다.
- 생성자: 리소스를 할당한다.
- 복사 생성자: 리소스를 복사해온다.
- 복사 대입 연산자: 기존 리소스를 해제하고 리소스를 복사해온다.
- (C++11부터) 이동 생성자: 리소스를 가져온다.
- (C++11부터) 이동 대입 연산자: 기존 리소스를 해제하고 리소스를 가져온다.
- 소멸자: 리소스를 해제한다.
Copy-and-swap idiom은 간결하면서도 강력한 예외 안정성(strong exception safety)을 보장해주는 복사 대입 연산자를 짤 수 있게 해준다. 또한 이동 대입 연산자에를 짤 때에도 유용하다.
문자열 리소스를 관리하는 간단한 클래스를 생각해보자.
class Foo
{
char* data = nullptr;
public:
Foo() : data(new char[14])
{
std::strcpy(data, "Hello, World!");
}
Foo(const Foo& other) : data(new char[std::strlen(other.data) + 1])
{
std::strcpy(data, other.data);
}
~Foo()
{
delete[] data;
}
};
아래는 잘못된 코드이다.
Foo& operator=(const Foo& other)
{
if (this != &other)
{
char* newData = new char[std::strlen(other.data) + 1];
std::strcpy(data, other.data);
delete[] data;
data = newData;
}
return *this;
}
std::swap을 이용하도록 하자.
Foo& operator=(Foo other) // note: argument passed by value
{
std::swap(data, other.data);
return *this;
}
중복되는 교환 부분을 따로 정의하면서, ADL을 이용해 std::swap을 확장하였다(std::swap을 구체화하는 것은 표준 라이브러리에 간섭하는 것이므로 좋지 않다.
friend void swap(Foo& fst, Foo& snd)
{
// enable ADL
using std::swap;
swap(fst.data, snd.data);
}
Foo(Foo&& other) : data(nullptr)
{
swap(*this, other);
}
이동 대입 연산자도 결국엔 대입이라 복사 대입 연산자와 크게 다르지 않다. 다른 점은 other를 복사하지 않는다는 점(리소스 할당이 없으므로 보통 예외가 발생하지 않는다)과 other가 곧 소멸될 것이라는 점이다.
class Foo
{
char* data;
public:
Foo() : data(new char[14])
{
std::strcpy(data, "Hello, World!");
}
Foo(const Foo& other) : data(new char[std::strlen(other.data) + 1])
{
std::strcpy(data, other.data);
}
Foo& operator=(Foo other) // note: argument passed by value
{
swap(*this, other);
return *this;
}
Foo(Foo&& other) : data(nullptr)
{
swap(*this, other);
}
Foo& operator=(Foo&& other)
{
swap(*this, other);
return *this;
}
~Foo()
{
delete[] data;
}
private:
friend void swap(Foo& fst, Foo& snd)
{
// enable ADL
using std::swap;
swap(fst.data, snd.data);
}
friend std::ostream& operator<<(std::ostream& os, const Foo& foo)
{
os << foo.data;
return os;
}
};
int main()
{
const Foo foo;
std::cout << foo << '\n';
return 0;
}
출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=stkov&logNo=220121638743
'프로그래밍 언어 > C++' 카테고리의 다른 글
C++ 비동기 (Asynchronous) 실행 (0) | 2022.08.17 |
---|---|
C++ 동기(synchronous)와 비동기(asynchronous) / 블로킹(blocking)과 논블로킹(non-blocking) (0) | 2022.08.16 |
C++ 위임 생성자 (delegating constructor) (0) | 2022.08.05 |
C++ NULL과 nullptr의 차이 (0) | 2022.08.04 |
C++ 가변 길이 배열 (Variable Length Array) (0) | 2022.08.02 |