Pointeri
Numele unui array este un pointer.
lista[5] == *(lista+5)
Array de pointer -> numele listei este un pointer către pointeri (dublă indirectare).
Array de obiecte
Putem crea array-uri cu obiecte de orice tip.
Ele pot fi inițializate
clasa lista[4] = {1, 2, 3, 4};
Sau neinițializate
clasa lista[4];
Pentru cazul inițializat avem nevoie de constructor care primește parametru întreg.
class cl{
int i;
public:
cl(int j) : i(j) {}
int get_i() { return 1; }
}
int main() {
cl ob[3] = {1, 2, 3} // initializatori
}
Pot fi inițializați și clase cu constructori cu mai mulți parametri:
clasa lista[3] = {clasa(1,2), clasa(2, 3), clasa(4, 5)};
Pointerul this
Orice funcție membru are pointerul this, care arată către obiectul asociat cu funcția respectivă.
this în alte funcții
- Funcțiile prieten nu au
this - Funcțiile statice nu au
this
Pointeri între bază și clase derivate
Aritmetica pe pointeri nu funcționează dacă incrementăm un pointer către bază și suntem în derivată.
Pointeri către membrii în clase
Putem aplica doar .* și ->*.
struct X {
int v;
int get() const { return v*2; }
};
int main() {
X x{5};
// data-member pointer
int X::* dp = &X::v;
std::cout << "v = " << x.*dp << "\n";
// function-member pointer
int * fp)( const = &X::get;
std::cout << "get() = " << (x.*fp)() << "\n";
return 0;
}
Apelul prin referință.
Nou în C++.
Dacă transmitem obiecte prin referință, nu se mai creează obiecte temporare, ci se lucrează direct prin obiectul transmis ca parametru.
Nu se map apelează constructorul de copiere și destructorul. La fel și la întoarcerea unei referințe
Referințe independente în clase
class ob{
int& x;
public:
ob(int a) : x(a) {cout << x << " " << a++ << " " << x << " ";}
void afis() { cout << x; }
};
int main(){
ob ob1(10); // 10 10 11
ob ob2(20); // 20 20 21
ob1.afis(); // 21
}
Const și volatile
Înlocuiește #define, care era bazat pe substituție de valoare.
const implică internal linkage (e vizibil nu mai în fișierul respectiv la linkare).
Trebuie dată o valoare la declararea const, singura excepție:
extern const int a;
Argumente de funcții, parametrii de întoarcere
Apel prin valoare cu const : parametru formal nu se schimbă în funcție.
const la întoarcere: valoarea returnată nu se poate schimba.
Obiectele temporare sunt const
class X{};
X foo() { return X(); }
void g1(X&) {}
void g2(const X&) {}
int main() {
g1(foo()); // eroare, const la referință nonconst
g2(foo()); // merge ok
}
Static const trebuie inițializat la declarare, nu poate fi inițializat prin constructor.
Declararea unei funcții cu const nu garantează că nu se modifică starea obiectului! Părți ale obiectului care sunt mutable pot fi modificate.
Nu pot fi apelate funcții non-const.
Pot fi apelate atât de obiecte const cât și de obiecte non-const.
Poate fi ignorat const-ul prin casting la pointer din tipul obiectului
class Y {
int i;
public:
Y();
void f() const;
}
Y::Y() : i(0) {}
void Y::f() const {
// i++ ar da eroare, const function
// C-style cast
((Y*)this) -> i++; // merge, this era in mod normal == const Y*
// C++ cast
(const_cast<Y*>(this))->i++; // merge
}
Mutable
class Y {
int i;
mutable int j;
public:
Y();
void f() const;
}
//
void Y::f() const {
// i++ ar da eroare
j++; // Merge!
}
Mutable și lambda expresii
Dacă capturăm o valoare prin valoare:
int i = 2;
auto f = [i]() {
i = i*2; // da eroare, i e constantă
return i;
}
Putem să schimbăm valoarea local în lambda expresie cu mutable.
int i = 2;
auto f = [i]() mutable {
i = i*2;
return i;
}
int main() {
int i = 2;
auto f = [i]() mutable {
i = i * 2;
cout << i << " ";
};
cout << i << " "; // 2
f(); // 4
cout << i << " "; // 2
}
Volatile
Lasă compilatorul să știe că variabila poate suferi modificări în afara controlului compilatorului (de exemplu, de către SO).
- utilizat în multithreading, multitasking, întreruperi
Nu se fac optimizări de cod
int x = 10;
while (x == 10) {} // deoarece x nu se schimbă în while, poate fi optimizat la while(true) {};
volatile int i = 10;
while(i == 10) {} // nu se face optimizare
Static
Obiecte statice
Sunt inițializate o singură dată, dar se distrug de-abia la finalul programului, indiferent de unde apar.
class X {
int i;
public:
X(int i = 0) : i(i) { cout << "C" << i << " ";}
~X() {cout << "D" << i << " ";}
};
void f() {
static X x1(10);
static X x2;
static X x3(10);
}
int main() {
f();
f();
f();
cout << "10 ";
}
Se va afișa
C10 C0 C10 10 D10 D0 D10
Pentru că obiectele statice sunt construite o singură dată.
Dacă ar exista și obiecte globale, acestea ar fi distruse ultimele.
:: se poate comporta astfel, atunci când există mai multe denumiri
int x = 100;
class X {
static int x;
static int y;
//
}
int X::x = 1;
int X::y = x + 1; // aici va fi folosit X::x, nu ::x, deci y = 2
// daca voiam y = 101
// int X::y = ::x + 1;
static în cazuri particulare
- Clasele nested pot avea membri statici.
- Clasele locale nu pot avea membri statici. (de exemplu în funcții)
// Static members & local classes
#include <iostream>
using namespace std;
// Nested class CAN have static data members:
class Outer {
class Inner {
static int i; // OK: nested (member) class can have static data
};
};
// Definition of the static member:
int Outer::Inner::i = 47;
// Local class CANNOT have static data members:
void f() {
class Local {
public:
// static int i; // Error: no place to define Local::i outside this scope
} x;
}
int main() {
Outer o;
f();
return 0;
}
Funcțiile membre statice nu sunt asociate unui obiect, așa că nu au pointer-ul this.