Acest FAQ este vorba de Agregate și Păstăi și cuprinde următoarele materiale:
class NotAggregate1
{
virtual void f() {} //remember? no virtual functions
};
class NotAggregate2
{
int x; //x is private by default and non-static
};
class NotAggregate3
{
public:
NotAggregate3(int) {} //oops, user-defined constructor
};
class Aggregate1
{
public:
NotAggregate1 member1; //ok, public member
Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment
private:
void f() {} // ok, just a private function
};
Ai prins ideea. Acum las's vezi cum agregate sunt speciale. Ei, spre deosebire de non-agregat clase, poate fi inițializat cu acolade {}
. Această inițializare sintaxa este cunoscut pentru tablouri, și am învățat că acestea sunt agregate. Deci, să's începe cu ei.
Atunci când un obiect de tip scalar (bool
, int
, char
, "dublu", indicatori, etc.) este valoare inițializat aceasta înseamnă că este inițializat cu " 0 "pentru acel tip (false pentru bool
, 0.0
pentru "dubla", etc.). Atunci când un obiect de tip clasă cu un utilizator-a declarat constructorul implicit este valoarea inițializat sale constructorul implicit este apelat. Dacă constructorul implicit este implicit definit, atunci toate nonstatic membrii sunt recursiv valoarea inițializat. Această definiție este imprecisă și un pic incorect, dar ar trebui să vă dea o idee de bază. O trimitere nu poate fi valoare inițializat. Valoare de inițializare pentru un non-agregat de clasă poate eșua dacă, de exemplu, la clasa a nu este cazul constructorul implicit.
Exemple de matrice de inițializare:
class A
{
public:
A(int) {} //no default constructor
};
class B
{
public:
B() {} //default constructor available
};
int main()
{
A a1[3] = {A(2), A(1), A(14)}; //OK n == m
A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
int Array1[1000] = {0}; //All elements are initialized with 0;
int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
//the elements in this case are not value-initialized, but have indeterminate values
//(unless, of course, Array4 is a global array)
int array[2] = {1, 2, 3, 4}; //ERROR, too many initializers
}
Acum las's vezi cum agregate clase poate fi inițializat cu bretele. Destul de mult la fel. În loc de elementele de matrice vom inițializa non-statice de date a membrilor, în ordinea apariției lor în definiția de clasă (toate sunt publice, prin definiție). Dacă sunt mai puține de initializare decât membrii, restul sunt cu valoare inițializat. Dacă este imposibil de a inițializa unul dintre membrii care nu au fost inițializate explicit, vom obține o compilare eroare. Dacă există mai mult de initializare decât este necesar, vom obține o compilare eroare la fel de bine.
struct X
{
int i1;
int i2;
};
struct Y
{
char c;
X x;
int i[2];
float f;
protected:
static double d;
private:
void g(){}
};
Y y = {'a', {10, 20}, {20, 30}};
În exemplul de mai sus y.c
este inițializat cu 'un'
, y.x.i1
cu 10
, y.x.i2
cu 20
, y.nu[0]
cu 20
, y.i[1] " cu " 30 " și " y.f
este valoarea inițializat, care este, inițializat cu 0.0
. Protejat membru static " d "nu este inițializat la toate, pentru că este "statică".
Agregate sindicatele sunt diferite în care puteți inițializa doar primul membru cu aparat dentar. Cred că, dacă sunt suficient de avansate in C++ pentru a lua în considerare chiar și cu ajutorul sindicatelor (utilizarea lor poate fi foarte periculos și trebuie să fie gândit cu atenție), te vei putea uita regulile pentru sindicate în standard de tine :).
Acum, că știm ce's special la agregate, las's încercați să înțelegeți restricții pe clase; că este, de ce sunt acolo. Trebuie să înțelegem că memberwise inițializare cu bretele presupune că clasa este nimic mai mult decât suma membrilor săi. Dacă un utilizator definite de constructor este prezent, înseamnă că utilizatorul are nevoie pentru a face unele de lucru suplimentar pentru a inițializa membrii, prin urmare, bretele de inițializare ar fi incorect. Daca funcții virtuale sunt prezente, aceasta înseamnă că obiectele din această clasă (pe cele mai multe implementări) un pointer la așa-numita vtable de clasa, care este stabilit în constructor, așa că pregătiți-inițializare ar fi insuficiente. Ai putea da seama restul de restricții într-un mod similar ca un exercițiu :).
Deci, destul despre agregate. Acum putem defini o mai stricte set de tipuri, pentru spirit, Păstăi
Exemple:
struct POD
{
int x;
char y;
void f() {} //no harm if there's a function
static std::vector<char> v; //static members do not matter
};
struct AggregateButNotPOD1
{
int x;
~AggregateButNotPOD1() {} //user-defined destructor
};
struct AggregateButNotPOD2
{
AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};
POD-clase, POD-sindicate, tipurile scalare, și tablouri de astfel de tipuri sunt denumite colectiv POD-tipuri.
Păstăile sunt speciale în mai multe moduri. Am'll oferi doar câteva exemple.
conținutul de obiect într-o matrice de char sau unsigned char, și apoi
memcpyconținutul înapoi în obiect, obiectul va organiza valoarea sa inițială. Nu act de faptul că nu există nici o astfel de garanție pentru obiectele de non-POD tipuri. De asemenea, puteți copia în condiții de siguranță POD obiecte cu memcpy
. Următorul exemplu presupune T este un POD-tip: char buf[N]; T obj; // obj inițializat la valoarea sa inițială memcpy(buf, &obj, N); // între aceste două apeluri la memcpy, // obj ar putea fi modificat memcpy(&obj, buf, N); // în acest moment, fiecare subobject de obj de tip scalar // deține valoarea sa inițială
f()
este bolnav-format întrucât g()
este bine format. Rețineți că Microsoft's compiler este prea liberal cu această regulă—se emite doar un avertisment în ambele cazuri.
int f()
{
struct NonPOD {NonPOD() {}};
goto eticheta;
NonPOD x;
eticheta:
return 0;
} int g() { struct POD {int i; char c;}; goto eticheta; CAPSULA x; eticheta: return 0; }
reinterpret_cast "de la" A*
a T*
și ia pointer la primul membru și vice-versa.
Lista merge pe și de pe... Definiția standard a unui agregat s-a schimbat puțin, dar's încă destul de mult la fel:
Un agregat este o matrice sau o clasă (punctul 9) cu nici un utilizator furnizate de constructori (12.1), nu bretele-sau-egal-de initializare pentru non-statică de date de membri (9.2), nr private sau protected non-statică de date de membri (Clauza 11), nu clase de bază (punctul 10), și nu de funcții virtuale (10.3). Ok, ce s-a schimbat?
- Anterior, un agregat ar putea avea nici o utilizatorul-a declarat constructori, dar acum se poate't au furnizat de utilizator constructori. Există o diferență? Da, există, pentru că acum puteți declara constructori și implicit i: struct Agregate { Agregate() = implicit; // cere compilatorului pentru a genera implicit punerea în aplicare }; Acest lucru este încă un agregat pentru un constructor (sau orice specială funcția de membru) care este implicit în prima declarație nu este furnizat de utilizator.
Acum un agregat nu poate avea nici o bretele-sau-egal-de initializare pentru non-statică de date de membri. Ce înseamnă acest lucru? Ei bine, acest lucru este doar pentru că cu acest nou standard, putem inițializa membrii direct în clasa astfel: struct NotAggregate { int x = 5; // valid in C++11 std::vector
s{1,2,3}; // valabil }; Folosind această caracteristică face clasa nu mai este un agregat pentru că's, practic, echivalentă cu furnizarea de propriile constructorul implicit. Deci, ce este un agregat n't se schimbă deloc. L's încă aceeași idee de bază, adaptate la noile caracteristici. Ce despre PODs? PODs a trecut printr-o mulțime de schimbări. O mulțime de normele anterioare despre Păstăi s-au relaxat în acest nou standard, și modul în care definiția este prevăzută în standard a fost schimbat radical. Ideea unui POD este de a capta, de fapt, două proprietăți distincte:
- Acesta susține static de initializare, și
Întocmirea unui POD în C++ vă oferă aceeași memorie layout ca o struct compilate într-C. Din acest motiv, definiția a fost împărțit în două concepte distincte: neînsemnat clase și standard-aspect clase, deoarece acestea sunt mult mai utile decât POD. Standard acum foarte rar folosește termenul de POD, preferând mai specifice neînsemnat și standard-aspect concepte. Noua definiție în esență, spune că un POD este o clasa care este atât de banal și de a standard-layout, și această proprietate trebuie să dețină recursiv pentru toate non-statică de date de membri: UN POD struct este un non-uniune de clasă, care este atât un banal clasă și un standard de clasă aspect, si nu are nici non-static membri de date de tip non-POD struct, non-POD uniune (sau o serie de astfel de tipuri). în mod Similar, un POD uniunea europeană este o uniune care este atât de banal clasă și un aspect standard de clasă, și a nu non-static membri de date de tip non-POD struct, non-POD uniune (sau o serie de astfel de tipuri). UN POD de clasă este o clasă care este fie un POD struct sau un POD uniunii. Las's a trece peste fiecare dintre aceste două proprietăți în detaliu separat. Banal clase
Neînsemnat este primul bunurilor menționate mai sus: trivial clase suport static de initializare. Dacă o clasă este trivial copyable (un superset de banal clase), este ok pentru a copia reprezentării sale pe loc cu lucruri de genul
memcpy
și să te aștepți ca rezultatul să fie același. Standardul definește un banal de clasă, după cum urmează: O trivial copyable clasă este o clasă care:— are nici un non-trivial copia constructori (12.8),
— are nici un non-trivial muta constructori (12.8),
— are nici un non-trivial copia operatori de atribuire (13.5.3, 12.8),
— are nici un non-trivial muta operatori de atribuire (13.5.3, 12.8), și
— are un banal destructor (12.4).
Un banal clasa este o clasa care are un banal constructorul implicit (12.1) și este trivial copyable.
[ Notă: În special, un trivial copyable sau banal clasa nu au funcții virtuale sau clase de bază virtuale.—sfârșitul notă ] Deci, ce sunt toate acele banale și non-triviale lucruri? O copia/muta constructor pentru clasa a X-a este banal, dacă nu este furnizat de utilizator și dacă
— clasa a X-a nr. funcții virtuale (10.3) și nu virtual clase de bază (10.1), și
— constructorul selectat pentru a copia/muta fiecare directă clasa de baza subobject este banal, și
— pentru fiecare non-statică de date de membru de X, care este de tip clasă (sau matrice acestora), constructorul selectat pentru a copia/muta membru este trivial;
în caz contrar copia/muta constructor este non-trivial. Practic, aceasta înseamnă că o copia sau muta constructor este banal, dacă nu este furnizat de utilizator, clasa a nimic virtual în ea, și această proprietate deține recursiv pentru toți membrii clasei și pentru clasa de bază. Definiția de un banal copy/mutare operatorul de atribuire este foarte similar, înlocuind pur și simplu cuvântul "constructor" cu "operatorul de atribuire". Un banal destructor are, de asemenea, o definiție similară, cu un plus de constrângere care se poate't fi virtual. Și încă o regulă similară există și pentru trivial default constructori, cu adăugarea că un constructor implicit este nu-trivial în cazul în care clasa are non-statice de date cu membrii bretele-sau-egal-de initializare, care ne'am văzut mai sus. Aici sunt câteva exemple pentru a clarifica totul:
// empty classes are trivial
struct Trivial1 {};
// all special members are implicit
struct Trivial2 {
int x;
};
struct Trivial3 : Trivial2 { // base class is trivial
Trivial3() = default; // not a user-provided ctor
int y;
};
struct Trivial4 {
public:
int a;
private: // no restrictions on access modifiers
int b;
};
struct Trivial5 {
Trivial1 a;
Trivial2 b;
Trivial3 c;
Trivial4 d;
};
struct Trivial6 {
Trivial2 a[23];
};
struct Trivial7 {
Trivial6 c;
void f(); // it's okay to have non-virtual functions
};
struct Trivial8 {
int x;
static NonTrivial1 y; // no restrictions on static members
};
struct Trivial9 {
Trivial9() = default; // not user-provided
// a regular constructor is okay because we still have default ctor
Trivial9(int x) : x(x) {};
int x;
};
struct NonTrivial1 : Trivial3 {
virtual void f(); // virtual members make non-trivial ctors
};
struct NonTrivial2 {
NonTrivial2() : z(42) {} // user-provided ctor
int z;
};
struct NonTrivial3 {
NonTrivial3(); // user-provided ctor
int w;
};
NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration
// still counts as user-provided
struct NonTrivial5 {
virtual ~NonTrivial5(); // virtual destructors are not trivial
};
Standard-aspect este cea de-a doua proprietate. Standardul menționează că acestea sunt utile pentru a comunica cu alte limbi, și că's pentru un standard de clasă aspect are aceeași memorie structura echivalentă C struct sau union. Aceasta este o altă proprietate care trebuie să dețină recursiv pentru membrii și toate clase de bază. Și, ca de obicei, nu virtual funcții sau clase de bază virtuale sunt permise. Asta ar face layout-ul, incompatibile cu C. Un aer profesional al serviciilor regula de aici este că standard-layout clase trebuie să aibă toate non-statică de date de membri, cu aceeași control acces. Anterior, acestea au trebuit să fie toate public, dar acum le puteți face private sau protected, atâta timp cât acestea sunt toate * privat sau toate protejate. Atunci când se utilizează moștenire, singurul* clasa în întreaga moștenire copac poate fi non-statică de date de membri, și primul non-statică de date de membru nu poate fi de o clasa de baza tip (acest lucru ar putea rupe aliasing norme), în caz contrar, se's nu este un standard-aspect de clasă. Acesta este modul în care definiția merge în text standard:
standard-structura de clasă este o clasă care:
— nu are non-static membri de date de tip non-standard-aspect de clasă (sau matrice de astfel de tipuri) sau de referință,
— nu are funcții virtuale (10.3) și nu virtual clase de bază (10.1),
— are același control acces (Clauza 11) pentru toate non-statice de date a membrilor,
— nu are non-standard-layout clase de bază,
— fie nu are non-statică de date de membri în cele mai multe derivate clasa a și cel mult o clasa de baza cu non-statice de date a membrilor, sau nu are clase de bază cu non-statică de date de membri, și
— nu are clase de bază de același tip ca și primul non-statice date membru.
standard-structura struct este un standard de clasă aspect definit cu clasa-cheie struct sau clasa-cheie class. standard-layout uniunii este un standard de clasă aspect definit cu clasa-cheie a uniunii. [ Notă: Standard-layout clase sunt utile pentru a comunica cu codul scris în alte limbaje de programare. Aspectul lor este specificat în 9.2.—sfârșitul notă ] Și să's vedem câteva exemple.
// empty classes have standard-layout
struct StandardLayout1 {};
struct StandardLayout2 {
int x;
};
struct StandardLayout3 {
private: // both are private, so it's ok
int x;
int y;
};
struct StandardLayout4 : StandardLayout1 {
int x;
int y;
void f(); // perfectly fine to have non-virtual functions
};
struct StandardLayout5 : StandardLayout1 {
int x;
StandardLayout1 y; // can have members of base type if they're not the first
};
struct StandardLayout6 : StandardLayout1, StandardLayout5 {
// can use multiple inheritance as long only
// one class in the hierarchy has non-static data members
};
struct StandardLayout7 {
int x;
int y;
StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok
};
struct StandardLayout8 {
public:
StandardLayout8(int x) : x(x) {} // user-provided ctors are ok
// ok to have non-static data members and other members with different access
private:
int x;
};
struct StandardLayout9 {
int x;
static NonStandardLayout1 y; // no restrictions on static members
};
struct NonStandardLayout1 {
virtual f(); // cannot have virtual functions
};
struct NonStandardLayout2 {
NonStandardLayout1 X; // has non-standard-layout member
};
struct NonStandardLayout3 : StandardLayout1 {
StandardLayout1 x; // first member cannot be of the same type as base
};
struct NonStandardLayout4 : StandardLayout3 {
int z; // more than one class has non-static data members
};
struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class
Cu aceste noi reguli mult mai multe tipuri pot fi Păstăi acum. Și chiar dacă un tip nu este POD, putem profita de unele dintre POD proprietăți separat (dacă acesta este doar un banal sau standard-layout).
Biblioteca standard are trăsături pentru a testa aceste proprietăți în antet <type_traits>
:
template <typename T>
struct std::is_pod;
template <typename T>
struct std::is_trivial;
template <typename T>
struct std::is_trivially_copyable;
template <typename T>
struct std::is_standard_layout;
Ne putem referi la Proiect C++14 standard pentru referință.
Acest lucru este reglementat în secțiunea 8.5.1
Agregate care ne dă următoarea definiție:
Un agregat este o matrice sau o clasă (punctul 9) cu nr furnizate de utilizator constructori (12.1), nr private sau protejate non-statică de date de membrii (Clauza 11), nu clase de bază (punctul 10), și nu de funcții virtuale (10.3).
Singura schimbare este acum adăugarea în clasa de membru de initializare nu face o clasa non-agregat. Deci următorul exemplu din C++11 agregate de inițializare pentru clasele cu statele în ritm de initializare:
struct A
{
int a = 3;
int b = 3;
};
nu a fost un agregat în C++11, dar este în C++14. Această schimbare este acoperit în N3605: Membru de initializare si agregate, care are următorul rezumat:
Bjarne Stroustrup și Richard Smith a ridicat o problemă despre agregate inițializare și membru-initializare nu funcționează împreună. Asta cartea își propune să rezolve problema prin adoptarea Smith's a propus formularea care elimină o restricție care agregate poate't au membru-de initializare.
Definiția de POD(simplu vechi date) struct este acoperit în secțiunea 9
Clase care spune:
UN POD struct110 este un non-uniune de clasă, care este atât un banal clasă și standard-aspect de clasă, și nu are nici non-static membri de date de tip non-POD struct, non-POD uniune (sau o serie de astfel de tipuri). În mod similar, un POD uniunea europeană este o uniune care este atât de banal clasă și o standard-aspect de clasă, și nu are nici non-static membri de date de tip non-POD struct, non-POD uniune (sau o serie de astfel de tipuri). Un POD de clasă este o clasă care este fie un POD struct sau un POD uniunii.
care este aceeași formulare ca C++11.
După cum sa menționat în comentarii pod se bazează pe definiția de standard-aspect și care a schimbat pentru C++14 dar asta a fost prin defect de rapoarte care au fost aplicate pentru C++14 după fapt.
Au fost trei DRs:
Deci, standard-layout a trecut de la această Pre C++14:
standard-structura de clasă este o clasă care:
- (7.1) nu are non-static membri de date de tip non-standard-aspect de clasă (sau matrice de astfel de tipuri) sau de referință,
- (7.2) nu are funcții virtuale ([clasa.virtual]) și nu virtual clase de bază ([clasa.mi]),
- (7.3) are același control acces (Clauza [clasa.de acces]) pentru toate non-statice de date a membrilor,
- (7.4) nu are non-standard-layout clase de bază,
- (7.5), fie nu are non-statică de date de membri în cele mai multe derivate clasa a și cel mult o clasă de bază cu non-statice de date a membrilor, sau a nu există clase de bază cu non-statică de date de membri, și
- (7.6) nu are nici o bază de clase de același tip ca și primul non-statice date membru.109
O clasa S este un standard-aspect de clasă în cazul în care:
- (3.1) nu are non-static membri de date de tip non-standard-aspect de clasă (sau matrice de astfel de tipuri) sau de referință,
- (3.2) nu are funcții virtuale și nu virtual clase de bază,
- (3.3) are același control acces pentru toate non-statice de date a membrilor,
- (3.4) nu are non-standard-layout clase de bază,
- (3.5) are cel mult o clasa de baza subobject de orice tip,
- (3.6) are toate non-statică de date de membri și bit-domenii în clasă și clase de bază în primul rând declarat în aceeași clasă, și
- (3.7) nu are nici un element al setului M(S) de tipuri ca o clasa de baza, în cazul în care pentru orice tip X, M(X) este definită după cum urmează.104 [ Notă: M(X) este un set de tipuri de toate non-baza-clasa subobjects care poate fi la un zero offset în X. — end notă ]
- (3.7.1) Dacă X este un non-ue de tip clasă cu nu (eventual moștenit) non-statică de date de membri, stabilit M(X) este gol.
- (3.7.2) Dacă X este un non-ue de tip clasă cu un non-statice date membre de tip X0 că este fie de dimensiunea zero sau este primul non-statică de date de membru de X (în cazul în care acest membru poate fi un anonim uniunea), set M(X) este format din X0 și elemente de M(X0).
- (3.7.3) Dacă X este o uniune de tip, set M(X) este unirea tuturor M(Ui) și setul conține toate Ui, unde fiecare Ui este tipul de ith non-statică de date de membru al X.
- (3.7.4) Dacă X este o matrice de tip cu tipul de element Xe, set M(X) este format din Xe și elemente de M(Xe).
- (3.7.5) Dacă X este o organizație non-class, non-matrice, set M(X) este gol.
puteți să vă rugăm să elaboreze următoarele reguli:
Am'll încerca:
a) standard-layout clase trebuie să aibă toate non-statică de date de membri, cu aceeași control acces
Ca's simplu: toate non-date statice membrii trebuie să toate fi "public", "privat", sau "protejată". Puteți't au un "public" și un "privat".
Raționamentul pentru a le duce la raționamentul pentru a avea o distincție între "aspect standard" și "nu aspectul standard", la toate. Și anume, pentru a da compiler libertatea de a alege cum să pună lucrurile în memorie. L's nu doar despre vtable indicii.
Când au standardizat C++ 98, au avut practic pentru a prezice modul în care oamenii ar pune în aplicare. În timp ce ei au avut destul de un pic de experiență în punerea în aplicare cu diverse arome de C++, nu au fost't despre anumite lucruri. Așa că au decis să fie precauți: da compilatoare cât mai multă libertate.
Ca's de ce definiția de POD în C++98 este atât de strictă. A dat C++ compilatoare mare latitudine de pe membru de layout pentru cele mai multe clase. Practic, POD tipuri au fost destinate pentru a fi cazuri speciale, ceva ce a scris în mod special pentru un motiv.
Când C++11 era lucrat, au avut mult mai multa experienta cu compilatoare. Și-au dat seama că C++ compiler scriitori sunt foarte leneș. Au avut toată libertatea asta, dar n-au't face ceva cu ea.
Regulile de aspect standard sunt mai mult sau mai puțin codifică o practică comună: cele mai multe compilatoare n't chiar trebuie să se schimbe prea mult dacă nimic, la toate pentru a le pune în aplicare (in afara de, poate, unele lucruri pentru tipul corespunzător de trăsături).
Acum, când a venit la "public" / "privat", lucrurile sunt diferite. Libertatea de a reordona care membrii sunt "publice" vs "privat" de fapt, nu contează pentru compilator, în special în depanare construiește. Și din punctul de aspect standard este că nu există compatibilitate cu alte limbi, vă poate't au aspectul fi diferite în debug vs presă.
Atunci nu's a faptului că acesta nu't într-adevăr rănit utilizator. Daca're face o încapsulate clasa, sunt șanse bune că toate datele membrii vor fi "privat" oricum. În general, don't expune date publice de membri pe deplin încapsulate tipuri. Deci, acest lucru ar fi doar o problemă pentru cei puțini utilizatori care nu vrei să faci asta, cine vrea asta divizie.
Deci's nu-i mare pierdere.
b) o singură clasă în întreaga moștenire copac poate fi non-statice de date a membrilor,
Motivul pentru aceasta revine la ce au standardizat standard layout din nou: o practică comună.
Nu's nu o practică comună, atunci când vine vorba de a avea doi membri ai unei moșteniri copac, care de fapt a stoca lucruri. Unii pun la clasa de bază înainte de derivate, alții o fac în alt mod. Ce mod ai pentru membrii daca au venit de la două clase de bază? Și așa mai departe. Compilatoare diferă foarte mult de la aceste întrebări.
De asemenea, datorită zero/unu/infinity regulă, o dată ce ai spus tu poate avea două clase cu membri, vă pot spune la fel de multe, după cum doriți. Acest lucru necesită adăugarea de o mulțime de aspect reguli pentru cum să se ocupe de acest lucru. Trebuie să spun cât de moștenire multiplă lucrări, care orelor lor de date înainte de alte clase, etc. Ca's o mulțime de reguli, pentru foarte puțin câștig material.
Puteți't face tot ce se't au funcții virtuale și un constructor implicit aspect standard.
și primul non-statică de date de membru nu poate fi de o clasa de baza tip (acest lucru ar putea rupe aliasing reguli).
Pot't într-adevăr-i vorbească. Am'nu sunt suficient de educați în C++'s aliasing reguli pentru a înțelege într-adevăr. Dar are ceva de-a face cu faptul că baza de membru va împărtăși aceeași adresă ca și clasă de bază în sine. Asta este:
struct Base {};
struct Derived : Base { Base b; };
Derived d;
static_cast<Base*>(&d) == &d.b;
Și că's, probabil, împotriva C++'s aliasing reguli. Într-un fel.
Cu toate acestea, ia în considerare acest lucru: cât de util ar putea avea capacitatea de a face acest lucru vreodată de fapt fi? Deoarece numai o clasă poate avea non-statică de date de membri, apoi Derivat
trebuie să fie de clasă (deoarece are o "Bază" ca un membru). Deci, "de Bază" trebuie sa fie gol (de date). Și dacă "de Bază" este gol, precum o clasa de baza... ce au un membru de date de la toate?
Deoarece "de Bază" este gol, nu are nici un stat. Deci, orice non-static funcții membre vor face ce vor face în funcție de parametrii lor, nu a lor "această" pointer.
Deci, din nou: nu-i mare pierdere.
Descărcați C++17 proiect final de Standard Internațional [aici][1]. Agregate C++17 se extinde și îmbunătățește agregate și agregate de inițializare. Biblioteca standard, de asemenea, include acum un std::is_aggregate de tip trăsătură de clasă. Aici este definiția oficială la secțiunea 11.6.1.1 și 11.6.1.2 (referințele interne elided):
Un agregat este o matrice sau o clasa cu
— nu furnizate de utilizator, explicit, sau moștenit constructori,
— nu, private sau protejate non-statice de date a membrilor,
— nu, funcții virtuale, și
— nu virtual, private sau protejate clase de bază.
[ Notă: Agregate de inițializare nu permite accesarea protejate și private în clasa de bază a membrilor sau constructori. —end notă ]
Elementele unui agregat sunt:
— pentru o matrice, elementele de matrice în creșterea indice scopul, sau
— pentru o clasă, clase de bază directe în declarația de ordine, urmate de cele directe non-statică de date de membri care nu sunt membri ai unui anonim uniunii, în declarația de ordine. Ce s-a schimbat?
- Agregatele pot avea acum publică, non-virtual clase de bază. În plus, aceasta nu este o cerință de bază clase să fie agregate. Dacă acestea nu sunt agregate, acestea sunt de listă-inițializare.
struct B1 // nu un agregat { int i1; B1(int a) : i1(a) { } }; struct B2 { int i2; B2() = implicit; }; struct M // nu un agregat { int m; M(int a) : m(o) { } }; struct C : B1, B2 { int j; M M; C() = implicit; }; C c { { 1 }, { 2 }, 3, { 4 } }; cout << "este C agregat?: " << (std::is_aggregate<C>::valoare ? 'Y' : 'N') << " i1: " << c.i1 << " i2: " << c.i2 << " j: " << c.j << " m.m: " << c.m.m << endl; //stdout: este C agregat?: Y, i1=1 i2=2 j=3 m.m=4 - Explicit implicit constructorii sunt de nepermis
struct D // nu un agregat { int i = 0; D() = implicit; explicit D(D const&) = implicit; }; - Moștenirea constructori sunt de nepermis
struct B1 { int i1; B1() : i1(0) { } }; struct C : B1 // nu un agregat { folosind B1::B1; };
**Trivial Clase** Definiția banal clasa a fost refăcut în C++17 pentru a aborda mai multe defecte care nu au fost abordate în C++14. Modificările au fost de natură tehnică. Aici este noua definiție la 12.0.6 (referințele interne elided): O trivial copyable clasa este o clasa:
— în cazul în care fiecare constructor de copiere, mutare constructor, copia operator de atribuire, și pentru a muta operator de atribuire este fie șterse sau banal,
— care are cel puțin un non-șters, constructor de copiere, mutare constructor, copia operator de atribuire, sau muta operator de atribuire, și
— care are o banal, non-elimină destructor.
Un banal clasa este o clasa care este trivial copyable și are una sau mai multe implicit constructori, toate din care sunt fie banale sau șterse și din care cel puțin una nu este șters. [ Notă: În special, un trivial copyable sau banal clasa nu au funcții virtuale sau clase de bază virtuale.—sfârșitul notă ] Modificări:- În C++14, pentru o clasă de a fi banal, clasa nu ar putea avea nici copia/muta constructor/operatori de atribuire care au fost non-trivial. Cu toate acestea, atunci implicit declared ca implicit constructor/operator ar putea fi non-trivial și încă defined fi șterse pentru că, de exemplu, clasa conținea o subobject de tip clasă care nu a putut fi copiat/mutat. Prezența unor astfel de non-trivial, definite-ca-elimină contractantul/operatorul va provoca întreaga clasă pentru a fi non-trivial. O problemă similară a existat cu destructori. C++17 clarifică faptul că prezența unor astfel de constructor/operatorii nu produce clasă să fie non-trivial copyable, prin urmare, non-trivial, și că o trivial copyable clasă trebuie să aibă un banal, non-elimină destructor. DR1734, DR1928
- C++14-a permis o trivial copyable clasa, prin urmare, un banal clasă, pentru a avea fiecare copiere/mutare constructor/operatorul de atribuire a declarat ca șterse. Dacă, cum ar fi de clasă a fost, de asemenea, aspectul standard, s-ar putea, cu toate acestea, fie din punct de vedere copiat/mutat cu std::memcpy`. Acest lucru a fost un semantic contradicție, pentru că, prin definirea cât mai șters toate constructor/operatori de atribuire, creatorul de clasa destinate în mod clar că class nu a putut fi copiat/mutat, încă din clasa îndeplinită definiția unui trivial copyable clasa. Prin urmare, în C++17 avem o nouă clauză care să ateste că trivial copyable clasă trebuie să aibă cel puțin unul banal, non-șters (deși nu neapărat accesibil publicului) copia/muta constructor/operatorul de atribuire. A se vedea N4148, DR1734
- Cea de-a treia modificare tehnică se referă la o problemă similară cu default constructori. În C++14, o clasa ar putea fi banal default constructori care au fost implicit definit ca șterse, dar încă să fie un banal clasa. Noua definiție clarifică faptul că un banal clasă trebuie să aibă cel puțin unul banal, non-elimină constructorul implicit. A se vedea DR1496 Standard-layout Clase Definiția standard-layout a fost refăcut, de asemenea, să abordeze rapoarte defect. Iar modificările au fost de natură tehnică. Aici este textul de la standard (12.0.7). Ca și mai înainte, referințele interne sunt elided: O clasa S este un standard-aspect de clasă în cazul în care:
— are nici non-static membri de date de tip non-standard-aspect de clasă (sau matrice de astfel de tipuri) sau de referință,
— nu are funcții virtuale și nu virtual clase de bază,
— are același control acces pentru toate non-statice de date a membrilor,
— are nici non-standard-layout clase de bază,
— are cel mult o clasa de baza subobject de orice tip,
— are toate non-statică de date de membri și bit-domenii în clasă și clase de bază în primul rând declarat în aceeași clasă, și
— nu are nici un element al setului M(S) de tip (definit mai jos), ca o clasa de baza.108
M(X) este definită după cum urmează:
— Dacă X este un non-ue de tip clasă cu nu (eventual moștenit) non-statică de date de membri, stabilit M(X) este gol.
— Dacă X este un non-ue de tip clasă a cărei primă non-statică de date de membru a tip X0 (în cazul în care acest membru poate fi un anonim uniunii), set M(X) este format din X0 și elemente de M(X0).
— Dacă X este o uniune de tip, set M(X) este unirea tuturor M(Ui) și setul conține toate Ui, unde fiecare Ui este tipul de ith non-statică de date de membru al X.
— Dacă X este o matrice de tip cu tipul de element Xe, set M(X) este format din Xe și elemente de M(Xe).
— Dacă X este un non-class, non-matrice, set M(X) este gol.
[ Notă: M(X) este un set de tipuri de toate non-baza-clasa subobjects care sunt garantate într-un standard de clasă aspect să fie la zero offset în X. —end notă ]
[ Exemplu:
—end de exemplu ]struct B { int i; }; // standard-layout clasa struct C : B { }; // standard-layout clasa struct D : C { }; // standard-layout clasa struct E : D { char : 4; }; // nu este un standard-layout clasa struct Q {}; struct S : Q { }; struct T : Q { }; struct U : S, T { }; // nu este un standard-layout clasa
- Acest lucru asigură că două subobjects care au același tip de clasă și care aparțin aceluiași cele mai obiectului derivat nu sunt alocate la aceeași adresă.
Modificări:
- A clarificat faptul că cerința ca o singura clasa din arbore de derivare "a" non-static membri de date se referă la o clasă în care astfel de date sunt membri declarat mai întâi, nu cursuri în cazul în care acestea pot fi moștenite, și a extins această cerință non-static câmpuri de biți. De asemenea, clarificat faptul că un standard-layout clasa "are cel mult o clasa de baza subobject de orice tip." Vezi DR1813, DR1881
- Definiția standard-layout nu a permis tipul de orice clasă de bază să fie de același tip ca și primul non-statice date membru. Este pentru a evita o situație în care un membru de date la offset zero are același tip ca orice clasa de baza. C++17 standard oferă o mai riguroasă, definiție recursivă de "set de tipuri de toate non-baza-clasa subobjects care sunt garantate într-un standard de clasă aspect să fie la zero offset" astfel încât să interzică astfel de tipuri de a fi tipul de orice clasa de baza. A se vedea DR1672, DR2120.
Notă: C++ comitetului pentru standarde destinate modificările de mai sus pe baza defect rapoarte pentru a aplica la C++14, deși nouă limbă nu este publicat în C++14 standard. Este în C++17 standard.
Acest lucru este încă devreme, așa că unele dintre acest răspuns se poate schimba în viitor. În urma restul de clar tema de această întrebare, sensul și utilizarea de agregate continuă să se schimbe cu fiecare standard. Există mai multe modificări esențiale la orizont.
În C++17, acest tip este încă un agregat:
struct X {
X() = delete;
};
Și, prin urmare, X{}
încă compilează pentru că este agregat de inițializare - nu un constructor de invocare. Vezi de asemenea și: Când este un constructor privat nu un constructor privat?](https://stackoverflow.com/q/37618213/2069064)
În C++20, restricția se va schimba de la necesită:
nu este furnizat de utilizator, "explicite", sau moștenit constructori
pentru a
utilizatorul nu a declarat sau a moștenit constructori
Acest lucru a fost adoptat în C++20 proiect de lucru. Nici "X", nici " C " în legătură cauză vor fi agregate în C++20.
Acest lucru face, de asemenea, pentru un efect yo-yo cu următorul exemplu:
class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};
În C++11/14, " B " a fost nu un agregat datorită clasa de baza, deci B{}
efectuează valoare de inițializare care solicită B::B()
care solicită O::O()
, la un punct în cazul în care acesta este accesibil. Acest lucru a fost bine format.
În C++17, " B " a devenit un agregat pentru clase de bază au fost acceptate, ceea ce a făcut B{}
agregat-inițializare. Acest lucru necesită copy-listă-inițializare un O
de {}
, dar din afara context de "B", în cazul în care acesta nu este accesibil. În C++17, aceasta este bolnav-format (auto x = B();
ar fi bine, totuși).
În C++20 acum, pentru că de la regula de mai sus se schimba, " B " încă o dată, încetează să mai fie un agregat (nu din cauza clasei de bază, dar din cauza utilizatorului,-a declarat constructorul implicit - chiar dacă l's implicit). Așa că ne-am're înapoi pentru a merge prin " B " 's constructor, și acest fragment devine mai bine format.
O problemă comună care apare este care doresc să folosească emplace()
-stil de constructori cu agregate:
struct X { int a, b; };
std::vector<X> xs;
xs.emplace_back(1, 2); // error
Acest lucru nu funcționează, pentru că emplace
va încerca în mod eficient pentru a efectua inițializarea `X(1, 2), care nu este valabil. Tipic soluție este de a adăuga un constructor pentru a "X", dar cu această propunere (în prezent de lucru drum prin Core), agregate eficient va fi sintetizate constructori care face dreptul de lucru - și să se comporte ca regulate constructori. Codul de mai sus va compila ca-este în C++20 (presupunând că această funcție este aprobată, ceea ce e foarte probabil).
În C++17, acest lucru nu compila:
template <typename T>
struct Point {
T x, y;
};
Point p{1, 2}; // error
Utilizatorii ar trebui să scrie propriile lor deducere ghid pentru toate agregat template-uri:
template <typename T> Point(T, T) -> Point<T>;
Dar cum acest lucru este într-un anumit sens "cel mai evident lucru" pentru a face, și este de fapt doar sabloane, limba va face acest lucru pentru tine. Această modificare a fost aprobată prin Evoluția în noiembrie 2018, deci exemplul de mai sus probabil va compila în C++20 (fără a fi nevoie pentru utilizator-cu condiția deducerii ghid).