C++
Wert und Referenzsemantik
Suche…
Tiefe Kopier- und Bewegungsunterstützung
Wenn ein Typ Wertsemantik haben möchte und Objekte, die dynamisch zugewiesen werden, speichern muss, muss der Typ bei Kopiervorgängen neue Kopien dieser Objekte zuordnen. Dies muss auch für die Kopierzuweisung erfolgen.
Diese Art des Kopierens wird als "tiefe Kopie" bezeichnet. Es nimmt effektiv, was sonst eine Referenzsemantik gewesen wäre, und wandelt sie in eine Wertesemantik um:
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;
}
};
Verschiebungs-Semantik erlaubt einem Typ wie Value
, um zu vermeiden, dass die referenzierten Daten wirklich kopiert werden. Wenn der Benutzer den Wert auf eine Weise verwendet, die eine Verschiebung auslöst, kann das "kopierte" Objekt von den Daten, auf die es verweist, leer bleiben:
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_);
}
};
In der Tat können wir einen solchen Typ sogar nicht kopierfähig machen, wenn wir tiefe Kopien verbieten möchten, während das Objekt dennoch verschoben werden kann.
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_);
}
};
Wir können sogar die Nullregel anwenden, indem Sie 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;
};
Definitionen
Ein Typ hat eine Wertesemantik, wenn sich der beobachtbare Zustand des Objekts von allen anderen Objekten dieses Typs funktional unterscheidet. Wenn Sie also ein Objekt kopieren, haben Sie ein neues Objekt, und Änderungen am neuen Objekt sind vom alten Objekt aus nicht sichtbar.
Die meisten grundlegenden C ++ - Typen haben eine Wertesemantik:
int i = 5;
int j = i; //Copied
j += 20;
std::cout << i; //Prints 5; i is unaffected by changes to j.
Die meisten in der Standardbibliothek definierten Typen haben auch eine Wertesemantik:
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.
Von einem Typ wird gesagt, dass er eine Referenzsemantik hat, wenn eine Instanz dieses Typs seinen beobachtbaren Status mit einem anderen Objekt (außerhalb des Objekts) gemeinsam nutzen kann, sodass durch das Bearbeiten eines Objekts der Status innerhalb eines anderen Objekts geändert wird.
C ++ - Zeiger haben eine Wertesemantik in Bezug auf das Objekt, auf das sie zeigen, aber sie haben Referenzsemantik in Bezug auf den Zustand des Objekts, auf das sie zeigen:
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 ++ - Referenzen haben auch Referenzsemantiken.