C++
Värde och referens Semantik
Sök…
Djup kopiering och flytta support
Om en typ vill ha värdesemantik, och den måste lagra objekt som är dynamiskt allokerade, då på kopieringsoperationer, måste typen tilldela nya kopior av dessa objekt. Det måste också göra detta för kopiering.
Denna typ av kopiering kallas en "djup kopia". Det tar effektivt det som annars skulle ha varit referenssemantik och förvandlar det till värdesemantik:
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;
}
};
Flytta semantik tillåter en typ som Value
att undvika att kopiera sina referenser. Om användaren använder värdet på ett sätt som provocerar en rörelse, kan det "kopierade" från objektet lämnas tomt från de uppgifter som det hänvisade till:
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_);
}
};
Vi kan till och med göra en sådan typ icke-kopierbar, om vi vill förbjuda djupa kopior och samtidigt låta objektet flyttas runt.
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_);
}
};
Vi kan till och med tillämpa regeln om noll genom att använda 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;
};
Definitioner
En typ har värdesemantik om objektets observerbara tillstånd funktionellt skiljer sig från alla andra objekt av den typen. Detta innebär att om du kopierar ett objekt har du ett nytt objekt och ändringar av det nya objektet kommer inte att synas på något sätt från det gamla objektet.
De flesta grundläggande C ++ -typer har värdesemantik:
int i = 5;
int j = i; //Copied
j += 20;
std::cout << i; //Prints 5; i is unaffected by changes to j.
De flesta typer av standardbibliotek definierade värden har också semantik:
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.
En typ sägs ha referenssemantik om en instans av den typen kan dela sitt observerbara tillstånd med ett annat objekt (externt), så att manipulering av ett objekt kommer att göra att staten ändras inom ett annat objekt.
C ++ -pekare har värdesemantik med avseende på vilket objekt de pekar på, men de har referenssemantik när det gäller tillståndet för objektet de pekar på:
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 ++ - referenser har referenssemantik också.