수색…
깊은 복사 및 이동 지원
유형이 값 의미론을 가지기를 원하고 동적으로 할당 된 객체를 저장해야하는 경우 복사 작업에서 유형은 해당 객체의 새 복사본을 할당해야합니다. 또한 사본 할당을 위해이 작업을 수행해야합니다.
이런 종류의 복사를 "딥 카피"라고합니다. 그것은 실제로 다른 경우라면 참조 의미론을 가져 와서 값 의미 화로 바꾼다.
struct Inner {int i;};
const int NUM_INNER = 5;
class Value
{
private:
Inner *array_; //Normally has reference semantics.
public:
Value() : array_(new Inner[NUM_INNER]){}
~Value() {delete[] array_;}
Value(const Value &val) : array_(new Inner[NUM_INNER])
{
for(int i = 0; i < NUM_INNER; ++i)
array_[i] = val.array_[i];
}
Value &operator=(const Value &val)
{
for(int i = 0; i < NUM_INNER; ++i)
array_[i] = val.array_[i];
return *this;
}
};
Move semantics는 Value
와 같은 유형을 허용하여 참조 된 데이터를 실제로 복사하지 않도록합니다. 사용자가 이동을 유발하는 방식으로 값을 사용하면 객체의 "copied"는 참조 된 데이터가 비어있게됩니다.
struct Inner {int i;};
constexpr auto NUM_INNER = 5;
class Value
{
private:
Inner *array_; //Normally has reference semantics.
public:
Value() : array_(new Inner[NUM_INNER]){}
//OK to delete even if nullptr
~Value() {delete[] array_;}
Value(const Value &val) : array_(new Inner[NUM_INNER])
{
for(int i = 0; i < NUM_INNER; ++i)
array_[i] = val.array_[i];
}
Value &operator=(const Value &val)
{
for(int i = 0; i < NUM_INNER; ++i)
array_[i] = val.array_[i];
return *this;
}
//Movement means no memory allocation.
//Cannot throw exceptions.
Value(Value &&val) noexcept : array_(val.array_)
{
//We've stolen the old value.
val.array_ = nullptr;
}
//Cannot throw exceptions.
Value &operator=(Value &&val) noexcept
{
//Clever trick. Since `val` is going to be destroyed soon anyway,
//we swap his data with ours. His destructor will destroy our data.
std::swap(array_, val.array_);
}
};
사실, 우리는 객체를 움직일 수있게하면서 딥 카피를 금지하고 싶다면 그런 타입을 카피 불가능하게 만들 수도 있습니다.
struct Inner {int i;};
constexpr auto NUM_INNER = 5;
class Value
{
private:
Inner *array_; //Normally has reference semantics.
public:
Value() : array_(new Inner[NUM_INNER]){}
//OK to delete even if nullptr
~Value() {delete[] array_;}
Value(const Value &val) = delete;
Value &operator=(const Value &val) = delete;
//Movement means no memory allocation.
//Cannot throw exceptions.
Value(Value &&val) noexcept : array_(val.array_)
{
//We've stolen the old value.
val.array_ = nullptr;
}
//Cannot throw exceptions.
Value &operator=(Value &&val) noexcept
{
//Clever trick. Since `val` is going to be destroyed soon anyway,
//we swap his data with ours. His destructor will destroy our data.
std::swap(array_, val.array_);
}
};
우리는 심지어 unique_ptr
의 사용을 통해 제로 규칙을 적용 할 수 있습니다.
struct Inner {int i;};
constexpr auto NUM_INNER = 5;
class Value
{
private:
unique_ptr<Inner []>array_; //Move-only type.
public:
Value() : array_(new Inner[NUM_INNER]){}
//No need to explicitly delete. Or even declare.
~Value() = default; {delete[] array_;}
//No need to explicitly delete. Or even declare.
Value(const Value &val) = default;
Value &operator=(const Value &val) = default;
//Will perform an element-wise move.
Value(Value &&val) noexcept = default;
//Will perform an element-wise move.
Value &operator=(Value &&val) noexcept = default;
};
정의
객체의 관찰 가능 상태가 해당 유형의 다른 모든 객체와 기능적으로 다른 경우 유형은 값 의미를가집니다. 즉, 개체를 복사하면 새 개체가 생기며 새 개체의 수정 사항은 이전 개체에서 전혀 볼 수 없습니다.
대부분의 기본 C ++ 유형은 값 의미를가집니다.
int i = 5;
int j = i; //Copied
j += 20;
std::cout << i; //Prints 5; i is unaffected by changes to j.
대부분의 표준 라이브러리 정의 유형은 값 의미 체계를 가지고 있습니다.
std::vector<int> v1(5, 12); //array of 5 values, 12 in each.
std::vector<int> v2 = v1; //Copies the vector.
v2[3] = 6; v2[4] = 9;
std::cout << v1[3] << " " << v1[4]; //Writes "12 12", since v1 is unchanged.
하나의 객체를 조작하면 상태가 다른 객체 내에서 변경되도록 해당 유형의 인스턴스가 관찰 가능한 상태를 다른 객체 (외부 객체)와 공유 할 수있는 경우 유형이 참조 의미를 갖습니다.
C ++ 포인터는 자신이 가리키는 객체와 관련하여 가치 의미론을 가지고 있지만 포인터가 가리키는 객체의 상태 와 관련하여 참조 의미를가집니다.
int *pi = new int(4);
int *pi2 = pi;
pi = new int(16);
assert(pi2 != pi); //Will always pass.
int *pj = pi;
*pj += 5;
std::cout << *pi; //Writes 9, since `pi` and `pj` reference the same object.
C ++ 참조에는 참조 의미 체계가 있습니다.