만약 어떤 객체에 대해서 복사를 금지 시키고 싶다면 어떻게 해야 할까?
단순히 해당 객체를 복사하는 함수를 구현하지 않으면 될까?
일반적으로는 그렇겠지만, (기본) 복사 생성자 및 복사 대입 연산자라면 이야기가 달라진다.
이들은 따로 만들지 않으면, 컴파일러가 자동으로 생성해버리기 때문이다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class HomeFroSale {
};
int main()
{
HomeFroSale h1, h2;
HomeFroSale h3(h1);
h2 = h1;
return 0;
}
즉, 위와 같은 상황에서 h1의 복사를 막는 방법은 무엇일까?
복사 생성자 및 복사 대입 연산자를 명시적으로 선언하고 실제 복사 동작은 하지 않으면?
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class HomeFroSale {
public:
int pdata;
HomeFroSale() { }; // 기본 생성자
HomeFroSale(int data): pdata(data) { };
~HomeFroSale() { }; // 소멸자
HomeFroSale(const HomeFroSale& rhs) { }; // 복사 생성자
HomeFroSale& operator=(const HomeFroSale& rhs) { HomeFroSale temp; return temp; }; // 복사 대입 연산자
};
int main()
{
HomeFroSale h1(10), h2;
HomeFroSale h3(h1);
h2 = h1;
cout << h2.pdata << "\n";
cout << h3.pdata << "\n";
return 0;
}
확실히 h1의 값이 복사되진 않지만, 성공적으로 컴파일 및 실행이 된다는 것이 문제점이다.
여기서 원하는 것은, 복사가 안되는 것보다도 복사 행위 자체를 아예 금지 시키는 방법이다.
항목 5) 의 마지막에도 잠깐 언급했지만, private 키워드를 이용하는 방법이 존재한다.
거기다가 구현 자체를 하지 않으면 클래스 멤버 함수 및 프렌드(friend) 함수에서도 접근이 불가능해진다.
즉, 아래와 같은 방법이다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class HomeFroSale {
public:
int pdata;
HomeFroSale() { }; // 기본 생성자
HomeFroSale(int data): pdata(data) { };
~HomeFroSale() { }; // 소멸자
private:
HomeFroSale(const HomeFroSale& rhs); // 복사 생성자
HomeFroSale& operator=(const HomeFroSale& rhs); // 복사 대입 연산자
};
int main()
{
HomeFroSale h1(10), h2;
HomeFroSale h3(h1);
h2 = h1;
cout << h2.pdata << "\n";
cout << h3.pdata << "\n";
return 0;
}
이렇게 하면 프렌드 함수나 멤버 함수에서의 접근도 링커가 막아준다.
이것으로도 충분하다면 상관없지만,
프렌드 및 멤버 함수에서의 접근을 링커 단계가 아니라 컴파일 단계에서 막아버리는 방법도 존재한다.
( 최종적으로 이 방법이 실제로 가장 많이 사용된다. )
class UnCopyable {
protected:
UnCopyable() { };
~UnCopyable() { };
private:
// 선언만 함으로서 friend를 통한 호출도 막음
UnCopyable(const UnCopyable&);
UnCopyable& operator=(const UnCopyable&);
};
class HomeFroSale : private UnCopyable{
public:
int pdata;
HomeFroSale() { }; // 기본 생성자
HomeFroSale(int data): pdata(data) { };
public:
void SomeFunc()
{
HomeFroSale h1(10), h2;
HomeFroSale h3(h1);
h2 = h1;
cout << h2.pdata << "\n";
cout << h3.pdata << "\n";
}
};
int main()
{
HomeFroSale test;
test.SomeFunc();
HomeFroSale h1(10), h2;
HomeFroSale h3(h1);
h2 = h1;
cout << h2.pdata << "\n";
cout << h3.pdata << "\n";
return 0;
}
보다시피 이전의 방법과는 다르게 링커 단계보다 빠른 컴파일 단계에서 내부 멤버 함수인 SomeFunc 에서 에러가 발생한다.
실제로 boost 라이브러리에서 noncopyable.hpp 에 구현되어서 사용되는 방식이다.
// Boost noncopyable.hpp header file --------------------------------------//
// (C) Copyright Beman Dawes 1999-2003. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/libs/utility for documentation.
#ifndef BOOST_CORE_NONCOPYABLE_HPP
#define BOOST_CORE_NONCOPYABLE_HPP
#include <boost/config.hpp>
namespace boost {
// Private copy constructor and copy assignment ensure classes derived from
// class noncopyable cannot be copied.
// Contributed by Dave Abrahams
namespace noncopyable_ // protection from unintended ADL
{
#ifndef BOOST_NONCOPYABLE_BASE_TOKEN_DEFINED
#define BOOST_NONCOPYABLE_BASE_TOKEN_DEFINED
// noncopyable derives from base_token to enable Type Traits to detect
// whether a type derives from noncopyable without needing the definition
// of noncopyable itself.
//
// The definition of base_token is macro-guarded so that Type Trais can
// define it locally without including this header, to avoid a dependency
// on Core.
struct base_token {};
#endif // #ifndef BOOST_NONCOPYABLE_BASE_TOKEN_DEFINED
class noncopyable: base_token
{
protected:
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) && !defined(BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS)
BOOST_CONSTEXPR noncopyable() = default;
~noncopyable() = default;
#else
noncopyable() {}
~noncopyable() {}
#endif
#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
noncopyable( const noncopyable& ) = delete;
noncopyable& operator=( const noncopyable& ) = delete;
#else
private: // emphasize the following members are private
noncopyable( const noncopyable& );
noncopyable& operator=( const noncopyable& );
#endif
};
}
typedef noncopyable_::noncopyable noncopyable;
} // namespace boost
#endif // BOOST_CORE_NONCOPYABLE_HPP
항목 8) 소멸자에서의 예외 발생 (0) | 2021.12.05 |
---|---|
항목 7) 가상 소멸자에 대해서 (0) | 2021.12.05 |
항목 5) C++가 자동으로 만들고 호출하는 함수들 (0) | 2021.12.05 |
항목 4) 객체 사용 전에 반드시 먼저 초기화하자 (0) | 2021.12.05 |
항목 3) const 에 대해서 (1) | 2021.08.15 |
댓글 영역