Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Klasy języka C++ są domyślnie typami wartości. Można je określić jako typy referencyjne, które umożliwiają zachowanie polimorficzne w celu obsługi programowania obiektowego. Typy wartości są czasami wyświetlane z perspektywy kontrolki pamięci i układu, natomiast typy referencyjne dotyczą klas bazowych i funkcji wirtualnych do celów polimorficznych. Domyślnie typy wartości można kopiować, co oznacza, że zawsze istnieje konstruktor kopii i operator przypisania kopiowania. W przypadku typów referencyjnych klasy nie można kopiować (wyłączać konstruktor kopii i operator przypisania kopiowania) i używać wirtualnego destruktora, który obsługuje ich zamierzony polimorfizm. Typy wartości dotyczą również zawartości, która po ich skopiowaniu zawsze daje dwie niezależne wartości, które można modyfikować oddzielnie. Typy referencyjne dotyczą tożsamości — jakiego rodzaju jest obiekt? Z tego powodu "typy referencyjne" są również określane jako "typy polimorficzne".
Jeśli naprawdę potrzebujesz typu przypominającego odwołanie (klasa podstawowa, funkcje wirtualne), musisz jawnie wyłączyć kopiowanie, jak pokazano w MyRefType
klasie w poniższym kodzie.
// cl /EHsc /nologo /W4
class MyRefType {
private:
MyRefType & operator=(const MyRefType &);
MyRefType(const MyRefType &);
public:
MyRefType () {}
};
int main()
{
MyRefType Data1, Data2;
// ...
Data1 = Data2;
}
Kompilowanie powyższego kodu spowoduje następujący błąd:
test.cpp(15) : error C2248: 'MyRefType::operator =' : cannot access private member declared in class 'MyRefType'
meow.cpp(5) : see declaration of 'MyRefType::operator ='
meow.cpp(3) : see declaration of 'MyRefType'
Typy wartości i wydajność przenoszenia
Unika się nakładu pracy związanego z alokacją kopiowania z powodu nowych optymalizacji kopiowania. Na przykład podczas wstawiania ciągu w środku wektora ciągów nie ma narzutów kopiowania, tylko ruch, nawet jeśli powoduje wzrost samego wektora. Te optymalizacje dotyczą również innych operacji: na przykład wykonanie operacji dodawania na dwóch ogromnych obiektach. Jak włączyć te optymalizacje operacji wartości? Kompilator włącza je niejawnie, podobnie jak konstruktory kopiowania mogą być generowane automatycznie przez kompilator. Jednak klasa musi "wyrazić zgodę", aby przenieść przypisania i przenieść konstruktory, deklarując je w definicji klasy. Funkcja Move używa odwołania double ampersand (&&) rvalue w odpowiednich deklaracjach funkcji składowych i definiowaniu konstruktora przenoszenia i metod przypisania przenoszenia. Należy również wstawić prawidłowy kod, aby "ukraść guts" z obiektu źródłowego.
Jak zdecydować, czy chcesz włączyć operacje przenoszenia? Jeśli wiesz już, że musisz włączyć konstrukcję kopii, prawdopodobnie chcesz włączyć budowę przenoszenia, zbyt, zwłaszcza jeśli jest tańsza niż kopia głęboka. Jeśli jednak wiesz, że potrzebujesz obsługi przenoszenia, niekoniecznie oznacza to, że chcesz włączyć operacje kopiowania. Ten ostatni przypadek jest nazywany "typem tylko do przenoszenia". Przykład już w standardowej bibliotece to unique_ptr
. Jak zauważasz, stary auto_ptr
jest przestarzały i został zastąpiony właśnie przez unique_ptr
brak obsługi semantyki przenoszenia w poprzedniej wersji języka C++.
Korzystając z semantyki przenoszenia, można zwrócić wartość po lub wstawić w środku. Przenoszenie to optymalizacja kopii. Nie ma potrzeby przydzielania sterty jako obejścia. Rozważmy następujący pseudokod:
#include <set>
#include <vector>
#include <string>
using namespace std;
//...
set<widget> LoadHugeData() {
set<widget> ret;
// ... load data from disk and populate ret
return ret;
}
//...
widgets = LoadHugeData(); // efficient, no deep copy
vector<string> v = IfIHadAMillionStrings();
v.insert( begin(v)+v.size()/2, "scott" ); // efficient, no deep copy-shuffle
v.insert( begin(v)+v.size()/2, "Andrei" ); // (just 1M ptr/len assignments)
//...
HugeMatrix operator+(const HugeMatrix& , const HugeMatrix& );
HugeMatrix operator+(const HugeMatrix& , HugeMatrix&&);
HugeMatrix operator+( HugeMatrix&&, const HugeMatrix& );
HugeMatrix operator+( HugeMatrix&&, HugeMatrix&&);
//...
hm5 = hm1+hm2+hm3+hm4+hm5; // efficient, no extra copies
Włączanie przenoszenia dla odpowiednich typów wartości
W przypadku klasy przypominającej wartość, w której przenoszenie może być tańsze niż kopia głęboka, włącz tworzenie przenoszenia i przypisywanie przenoszenia w celu zwiększenia wydajności. Rozważmy następujący pseudokod:
#include <memory>
#include <stdexcept>
using namespace std;
// ...
class my_class {
unique_ptr<BigHugeData> data;
public:
my_class( my_class&& other ) // move construction
: data( move( other.data ) ) { }
my_class& operator=( my_class&& other ) // move assignment
{ data = move( other.data ); return *this; }
// ...
void method() { // check (if appropriate)
if( !data )
throw std::runtime_error("RUNTIME ERROR: Insufficient resources!");
}
};
Jeśli włączysz tworzenie/przypisywanie kopii, włącz również przenoszenie konstrukcji/przypisania, jeśli może być tańsze niż kopia głęboka.
Niektóre typy inne niż wartość są przenoszone tylko wtedy, gdy nie można sklonować zasobu, tylko przenieść własność. Przykład: unique_ptr
.
Zobacz też
System typów języka C++
Witamy z powrotem w języku C++
Dokumentacja języka C++
Standardowa biblioteka C++