Zoeken…


Ondersteuning voor diep kopiëren en verplaatsen

Als een type waarde-semantiek wil hebben, en het moet objecten opslaan die dynamisch worden toegewezen, dan moet het type bij kopieerbewerkingen nieuwe kopieën van die objecten toewijzen. Het moet dit ook doen voor kopie-toewijzing.

Dit soort kopiëren wordt een "diepe kopie" genoemd. Het neemt effectief wat anders referentie-semantiek zou zijn geweest en zet het om in waarde-semantiek:

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;
  }
};
C ++ 11

Met Move-semantiek kan een type als Value voorkomen dat de gegevens waarnaar wordt verwezen echt worden gekopieerd. Als de gebruiker de waarde op een manier gebruikt die een verplaatsing uitlokt, kan de "gekopieerde" van het object leeg blijven van de gegevens waarnaar wordt verwezen:

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_);
  }
};

Inderdaad, we kunnen zelfs zo'n type niet-kopieerbaar maken, als we diepe kopieën willen verbieden terwijl we het object nog steeds kunnen verplaatsen.

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_);
  }
};

We kunnen zelfs de Rule of Zero toepassen, door het gebruik van 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;
};

Definities

Een type heeft waarde-semantiek als de waarneembare status van het object functioneel verschilt van alle andere objecten van dat type. Dit betekent dat als u een object kopieert, u een nieuw object hebt en dat wijzigingen van het nieuwe object op geen enkele manier zichtbaar zijn vanaf het oude object.

De meeste standaard C ++ -typen hebben waarde-semantiek:

int i = 5;
int j = i; //Copied
j += 20;
std::cout << i; //Prints 5; i is unaffected by changes to j.

De meeste door de standaardbibliotheek gedefinieerde typen hebben ook waarde-semantiek:

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.

Van een type wordt gezegd dat het referentiesemantiek heeft als een instantie van dat type zijn waarneembare status kan delen met een ander object (extern), zodat het manipuleren van een object de status binnen een ander object zal veranderen.

C ++ -aanwijzers hebben waarde-semantiek met betrekking tot welk object ze verwijzen, maar ze hebben referentie-semantiek met betrekking tot de status van het object waarnaar ze wijzen:

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 ++ referenties hebben ook referentiesemantiek.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow