C++11
Michelangelo DiligentiIngegneria Informatica e
dell'Informazione
C++11● Nuova revisione dello standard dopo il C++98
– Stabilita nel 2011 dopo anni di lavoro
– I compilatori la supportano da non molto tempo
● Tantissime novita'– Nuove librerie
– Nuove features
– Cambia molto l'uso del linguaggio anche in cose fondamentali come il ritorno dei valori
● Copriremo solo alcuni aspetti– Per un testo completo vedete risorse online o
Effective Modern C++, Scott Meyers
Auto● Deduzione automatica di tipo, esempi
– auto x = 0; // x intero
auto x = 0.0; // x double
auto x; // ERRORE tipo non determinabile
– map<string, int> mappa;
auto iter = mappa.begin(); // prendo iteratore
– const map<string, int> mappa;
auto iter = mappa.begin(); // prendo iteratore const
– Posso unire a const e references per forzare constness od assicurare la copia per riferimento
const auto& nome = oggetto.GetNome();
Range loops● Possibile definire iterazione su contenitori senza
usare gli iteratori
vector<int> v;
for (int i : v) { cout << i << endl; }
map<string, int> m;
for (std::pair<string, int> p : m) {
cout << p.first << “:“ << p.second << endl;
}
Range loops● Range loop spesso usati insieme ad auto
vector<int> v;
// accesso per valore, i e' copia dei valori nel vettore
for (auto i : v) {}
map<string, int> m;
// p passato per valore, p e' copia del pair<string, int>
for (auto p : m) {
cout << p.first << “ “ << p.second << endl;
}
Range loops● Auto permette grande flessibilita', ad esempio posso
forzare constness e/o copia per riferimento
vector<int> v;
for (const auto& i : v) {...} // i adesso e' const int&
map<string, int> m;
// Accesso per const reference a std::pair<const std::string, int>
for (const auto& p : m) {
cout << p.first << “ “ << p.second << endl;
}
Braced initialization● C++98 inconsistente nella gestione delle inizializzazioni
int i = 0;int i(0); // come sopravector<int> v(10);vector<int> v = vector<int>(10); // come sopra
● C++11 possibile inizializzare gli oggetti in modo consistente tramite {}
int i{0}; vector<int> v{10};// Array const con 5 elementi subito inizializzaticonst float* vec = new const float[5]{1,2,3,4,5};
Braced initialization● Permette di fare anche inizializzazioni complesse● Liste di parametri nel caso generale (vi rimando al
testo per questo)● Le seguenti sono inizializzazioni valide in C++11
– Inizializzare un vettore di 5 interivector<int> v = {3,2,1,5,4};
– mappa con 2 valori inizialimap<string,int> m = {{"C++98",1998}, {"C++11",i}};
nullptr vs NULL● In C++98 NULL e' semplicemente
#define NULL 0
– NULL puo' essere interpretato come un intero● Non ha un tipo esplicito● Il seguente codice compila (al massimo puo' dare un
warning il compilatore)
int i = NULL + 4;
● C++11 specifica nullptr che non e' convertibile in un intero
– Si usa come NULL
Class * c = nullptr;
if (c == nullptr) { … }
●
Deleted methods
● In C++98 si evita la copia di un oggetto definendo costruttori di copia e operator= come privati
private:
Pippo(const Pippo&) {}
Pippo& operator=(const Pippo&) {} …
● In C++11 si specifica che tali metodi sono deleted
public:
Pippo(const Pippo&) = delete;
Pippo& operator=(const Pippo&) = delete;
Default methods● Possibile dire al compilatore in modo esplicito quali
metodi esso deve generare– Applicabile a default, copy e move (vederemo cosa
sia) construtor, destructor, operator=
class Pippo {
Pippo(int a) { … }
// Default constructor non sarebbe generato dal
// compilatore perche' c'e' Pippo(int), ma dico di generarlo
Pippo() = default;
Pippo(const Pippo& p) = default;
};
Constructor Delegation● In C++98 si puo' invocate un costruttore del padre in
pre-run, ma– Non si puo' invocare altro costruttore della classe
– Problema: Non si riusa codice dei costruttori
● In C++11 possibile chiamare pre-run altri costruttori– Riutilizzo del codice possibile anche per i costruttori
class Pippo {
Pippo(const int a) { … }
Pippo() : Pippo(0) {…}
Pippo(const float b) : Pippo(static_cast<int>(b)) {…}
};
Inizializzazione in-class● Possibile inizializzare i dati membri in fase di
dichiarazione senza dover passare dal costruttore– Finalmente! Codice chiaro, leggibile e sicuro
– Non rimane mai qualcosa di non inizializzato
class Pippo {
string* s = nullptr;
const static int x = 5;
int y = 5; // o int y{5};
vector<int> myVec{1,2,3,4,5};
};
Lambda functions● Funzioni senza nome create nel posto dove
servono● Esempio sort vettore in C++98
struct Comparator {
bool operator()(int v, int w){ return v > w; }
};
sort(vec.begin(), vec.end(), Comparator());
● In C++11 con lambda function:
sort(vec.begin(),vec.end(), [](int v,int w){ return v > w; });
Lambda functions
● Lambda function: sintassi– [variabili_catturate](argomenti) { … la funzione … }
● Variabili catturate sono le variabili locali nel chiamante che sono usate nella lambda
● Default passate per valore. Possibile passarle per reference per prendere il risultato: Esempio:
vector<int> v{1,2,3,4,5}; int sum = 0;
// sum passata per reference dal contesto locale. Valore di sum dopo la // chiamata = 15 (1+2+3+4+5) for_each(v.begin(),v.end(),[&sum](int v){sum += v;});
// sum passato per copia, suo valore resta 0 esternamente for_each(v.begin(),v.end(),[sum](int v){sum += v;});
Move semantics● Passo fondamentale del C++11
– Evita il problema del C++98 della copia dei dati locali di una funzione o metodo!
● Esempio ritornare una matrice grande– C++98
● Matrix* Alloca(); // puntatore, poi devo deallocare● void Alloca(Matrix*); // OK ma poco naturale
– C++11● Matrix Alloca(); // posso evitare la copia!
● Move semantic permette di definire come spostare un dato da una variabile temporanea ad un'altra
Move semantics: lvalue, rvalue● lvalue: valore di cui posso prendere indirizzo
– permanente rispetto all'espressione valutata
● rvalue: valore che esiste solo durante la valutazione dell'espressione
● Esempio 1:
int x = 1; // x lvalue, 1 rvalue
Move semantics: lvalue, rvalue● Esempio 2:
Matrix Alloca() {
Matrix m(100,100);
return m; // m e' variabile temporanea, rvalue
}
● Esempio 3
static Matrix m;
Matrix Alloca() {
m.Init(100, 100);
return m; // m statica e permanente, lvalue
}
Move semantics: reference &● Reference classica & e' applicabile solo a lvalues
– Matrix& Alloca() {
Matrix m(100,100);
// disastro in vista, riferimento a rvalue che viene distrutto
return m;
}
– static Matrix m;
Matrix& Alloca() {
m.Init(100, 100);
return m; // m statica e permanente, riferimento OK
}
Move semantics: &&, move constructor● Reference && solo applicabile ad rvalues
– Posso specificare una specie di copy constructor per rvalues
– rvalue e' temporaneo non serve fare una vera operazione di copia ma solo di spostamento!
● Per oggetti grandi tale operazione e' spesso molto piu' rapida
● Move constructor: invocato se si passa per valore un rvalue
class Pippo {
Pippo(const Pippo& p) { … } // copy constructor
Pippo(Pippo&& p) { … } // move constructor
};
Move semantics: &&, move constructor● Esempio di move constructor
class Matrix { … int N, M; float* data;
Matrix(Matrix&& m) { // move constructor N=m.N; M=m.M;
// La rappresentazione dell'rvalue viene “rubata”, e si // riutilizza la memoria precedentemente allocata. data = m.data;
m.data = nullptr; m.N=m.M=0; // svuotare il vecchio matrix
} };
– Esempio di utilizzo Matrix Alloca() { Matrix m(100, 100); return m; }
Matrix m = Alloca();
Si ritorna l'rvalue per valore, compilatore invoca il move constructor. Massima velocita' e pulizia dell'interfaccia.
Move semantics: &&, move constructor● Move constructor veloce vs Copy constructor lento
class Matrix { … Matrix(Matrix&& m) { // move constructor N=m.N; M=m.M;
// La rappresentazione dell'rvalue viene “rubata”, si copia // solo 1 puntatore data = m.data; m.data=nullptr; m.M=m.N=0;
}
Matrix(const Matrix& m) { // copy constructor N=m.N; M=m.M;
// Assumo implementazione con singolo vettore contiguo data = new float[N*M]; // devo riallocare, lento!
for (int i = 0; i < N*M; ++i) // copia elemento ad elemento data[i] = m.data[i];
} ...
Move semantics: contenitori● Contenitori stl copiano in inserimento
– std::move() trasforma una lvalue in un rvalue● Permette di chiamare move semantics in inserimento!
– C++98vector<string> v;
string s(“ciao”);
v.push_back(s); // v prende una copia di s
cout << s; // stampa “ciao”
– C++11: Move semantics permette di evitare la copia
vector<string> v;
string s(“ciao”);
v.push_back(std::move(s)); // s “rubata” dal contenitore
cout << s; // stampa “”, la rappresentazione di s e' stata rubata ed e' in v
std::move(s) crea string&& Compilatore invoca il move constructor
Move semantics: &&, move constructor● std::move usabile in ogni contesto ad esempio nel
caso precedente di move constructorclass Matrix { … int N, M; float* data;
Matrix(Matrix&& m) : data(std::move(m.data)) { // move constructor
N=m.N; M=m.M;
m.data=nullptr;
m.N=m.M=0;
} };
Librerie: smart pointers● std::unique_ptr puntatore con ownership unica
#include <memory>
{
std::unique_ptr<int> ptr(new int(42));
std::cout << ptr.get() << std::endl; // indirizzo
std::cout << *ptr << std::endl; // 42
std::unique_ptr<int> second = first; // compile error
// ptr distrutto automaticamente qui
}
Librerie: smart pointers● std::shared_ptr puntatore con ownership multipla
– Implementa garbage collector locale
#include <memory>
{
std::shared_ptr<int> ptr1(new int);
std::cout << ptr1.use_count() << std::endl; // 1
std::shared_ptr<int> ptr2(ptr1);
std::cout << ptr1.use_count() << std::endl; // 2
std::cout << ptr2.use_count() << std::endl; // 2
}
Librerie: contenitori associativi● Contenitori associativi non ordinati
– Hash tables
– Permettono ricerche in O(1)
– In particolare:● unordered_set<T>● unordered_multiset<T>● unordered_map<K,V>● unordered_multimap<K, V>
Librerie: contenitori● array<T>
– Vettore a dimensione fissa
– Stessa interfaccia di vector (anche come iteratori, e quindi algoritmi chiamabili, ecc)
array<int, 5> a{{1,2,3,4,5}};
int sum = 0;
for_each(a.begin(),a.end(),[&sum](int e){sum+=e;});
Librerie: tuple● std::tuple<T1, T2, T3, ...>
– Estendono i std::pair<T1, T2> del C++98
– Contenitore eterogeneo di dimensione fissa
// Costruttore
std::tuple<std::string,int,float> t{"ciao", 10, 5.1};
// come make_pair per la tupla. Deduce i tipi in // modo automatico
auto t = std::make_tuple(100, "ciao", 2011,'c');
// Prende un elemento della tupla
std::cout << get<2>(t);
Molte altre cose● Ottimizzazione: constexpr● Template flessibili:
– alias vs typedef, variadic templates, decltype
● Enum tipati: scoped enum● Controllo sull'ereditarieta': final, override● Librerie
– regular expressions, multithreading support– Moderne librerie per time e numeri casuali
● Utilita' generiche: static_assert, raw strings● ecc.
Top Related