Jednolite inicjowanie i delegowanie konstruktorów
W nowoczesnych C++, można użyć inicjowania nawias klamrowy dla dowolnego typu, bez znaku równości.Również można użyć konstruktorów delegując uproszczenie kodu, gdy masz wiele konstruktorów, które wykonują podobne prace.
Inicjowanie nawias klamrowy
Nawias klamrowy inicjowania można użyć dla dowolnego klasy, struktury lub Unii.Jeśli typ ma domyślnego konstruktora jawnie lub niejawnie zadeklarowanej, można użyć domyślnego inicjowania nawias klamrowy (z pustych nawiasów klamrowych).Na przykład poniższa klasa może zainicjować za pomocą domyślnych i inicjowania nawias klamrowy innych niż domyślne:
#include <string>
using namespace std;
class class_a {
public:
class_a() {}
class_a(string str) : m_string{ str } {}
class_a(string str, double dbl) : m_string{ str }, m_double{ dbl } {}
double m_double;
string m_string;
};
int main()
{
class_a c1{};
class_a c1_1;
class_a c2{ "ww" };
class_a c2_1("xx");
// order of parameters is the same as the constructor
class_a c3{ "yy", 4.4 };
class_a c3_1("zz", 5.5);
}
Jeśli klasa ma konstruktorów innej niż domyślna, kolejność w klasy, które elementy są wyświetlane w inicjator nawias klamrowy jest kolejność wyświetlania odpowiednich parametrów w konstruktorze, nie zamówienia, w którym elementy członkowskie są uznane za (podobnie jak w przypadku class_a w poprzednim przykładzie).W przeciwnym razie jeśli typ posiada deklarowanych Konstruktor, kolejność wyświetlania członków w inicjatorze nawias klamrowy jest taka sama jak kolejność, w której są one zgłoszone; w takim przypadku można zainicjować dowolnej liczby członków publicznych jak chcesz, ale nie można pominąć któregokolwiek członka.Poniższy przykład przedstawia zamówienia, który jest używany podczas inicjowania nawias klamrowy, gdy nie ma żadnych deklarowanych Konstruktor:
class class_d {
public:
float m_float;
string m_string;
wchar_t m_char;
};
int main()
{
class_d d1{};
class_d d1{ 4.5 };
class_d d2{ 4.5, "string" };
class_d d3{ 4.5, "string", 'c' };
class_d d4{ "string", 'c' }; // compiler error
class_d d5("string", 'c', 2.0 }; // compiler error
}
Jeśli Konstruktor domyślny jawnie została zadeklarowana, lecz oznaczone jako usunięte, nie można użyć domyślnego nawias klamrowy inicjowania:
class class_f {
public:
class_f() = delete;
class_f(string x): m_string { x } {}
string m_string;
};
int main()
{
class_f cf{ "hello" };
class_f cf1{}; // compiler error C2280: attempting to reference a deleted function
}
Można użyć nawiasów klamrowych inicjowania gdziekolwiek zazwyczaj zrobi inicjowania — na przykład, jako parametr funkcji lub wartości zwracanej, lub z new słowo kluczowe:
class_d* cf = new class_d{4.5};
kr->add_d({ 4.5 });
return { 4.5 };
initializer_list konstruktory
initializer_list Class Reprezentuje listę obiektów określonego typu, które mogą być używane w Konstruktorze i w innych kontekstach.Initializer_list można utworzyć za pomocą nawiasów klamrowych inicjowania:
initializer_list<int> int_list{5, 6, 7};
Ważne |
---|
Aby użyć tej klasy, należy dołączyć <initializer_list> nagłówka. |
initializer_list Mogą być kopiowane.W takim przypadku Członkowie nowe listy są odwołaniami do członków oryginalnej listy:
initializer_list<int> ilist1{ 5, 6, 7 };
initializer_list<int> ilist2( ilist1 );
if (ilist1.begin() == ilist2.begin())
cout << "yes" << endl; // expect "yes"
Standardowa biblioteka klas kontenera, a także string, wstring, i regex, ma initializer_list konstruktorów.Następujące przykłady przedstawiają metody inicjowania te konstruktory przytwierdza się do:
vector<int> v1{ 9, 10, 11 };
map<int, string> m1{ {1, "a"}, {2, "b"} };
string s{ 'a', 'b', 'c' };
regex rgx{'x', 'y', 'z'};
Konstruktory delegujące
Wiele klas ma wiele konstruktorów wykonujące podobne rzeczy — na przykład sprawdza poprawność parametrów:
class class_c {
public:
int max;
int min;
int middle;
class_c() {}
class_c(int my_max) {
max = my_max > 0 ? my_max : 10;
}
class_c(int my_max, int my_min) {
max = my_max > 0 ? my_max : 10;
min = my_min > 0 && my_min < max ? my_min : 1;
}
class_c(int my_max, int my_min, int my_middle) {
max = my_max > 0 ? my_max : 10;
min = my_min > 0 && my_min < max ? my_min : 1;
middle = my_middle < max && my_middle > min ? my_middle : 5;
}
};
Powtarzające się kod można zredukować przez dodanie funkcji, która realizuje pozostałe sprawdzanie poprawności, ale kod class_c będzie można łatwiej zrozumieć i utrzymania, jeśli jeden konstruktor może delegować część pracy potrzebnej do drugiego.Aby dodać delegując konstruktorów, użyj constructor (. . .) : constructor (. . .) składni:
class class_c {
public:
int max;
int min;
int middle;
class_c(int my_max) {
max = my_max > 0 ? my_max : 10;
}
class_c(int my_max, int my_min) : class_c(my_max) {
min = my_min > 0 && my_min < max ? my_min : 1;
}
class_c(int my_max, int my_min, int my_middle) : class_c (my_max, my_min){
middle = my_middle < max && my_middle > min ? my_middle : 5;
}
};
int main() {
class_c c1{ 1, 3, 2 };
}
W miarę przechodzenia poprzedniego przykładu, należy zauważyć, że konstruktora class_c(int, int, int) po raz pierwszy wywołuje konstruktor class_c(int, int), który z kolei wywołuje class_c(int).Każdy z konstruktorów wykonuje tylko pracy, która nie jest wykonywana przez innych konstruktorów.
Pierwszy konstruktora, który jest nazywany inicjuje obiekt tak, aby wszyscy jego członkowie są inicjowane w tym punkcie.Członek inicjalizacji w konstruktorze, które deleguje do innego konstruktora nie można zrobić, jak pokazano poniżej:
class class_a {
public:
class_a() {}
// member initialization here, no delegate
class_a(string str) : m_string{ str } {}
//can’t do member initialization here
// error C3511: a call to a delegating constructor shall be the only member-initializer
class_a(string str, double dbl) : class_a(str) , m_double{ dbl } {}
// only member assignment
class_a(string str, double dbl) : class_a(str) { m_double = dbl; }
double m_double{ 1.0 };
string m_string;
};
W następnym przykładzie pokazano sposób użycia inicjatory członka danych statycznych.Należy zauważyć, że jeśli Konstruktor inicjuje również członkiem dostarczone dane, zastąpić Inicjator członka:
class class_a {
public:
class_a() {}
class_a(string str) : m_string{ str } {}
class_a(string str, double dbl) : class_a(str) { m_double = dbl; }
double m_double{ 1.0 };
string m_string{ m_double < 10.0 ? "alpha" : "beta" };
};
int main() {
class_a a{ "hello", 2.0 }; //expect a.m_double == 2.0, a.m_string == "hello"
int y = 4;
}
Składnia delegowania Konstruktor nie zapobiec tworzeniu przypadkowego rekursji Konstruktor — Constructor1 Constructor2, który z kolei wywołuje Constructor1 wywołuje — i nie są generowane błędy momentu przepełnienia stosu.Jest odpowiedzialny za uniknąć cykli.
class class_f{
public:
int max;
int min;
// don't do this
class_f() : class_f(6, 3){ }
class_f(int my_max, int my_min) : class_f() { }
};