Gibt es eine andere Möglichkeit, eine bestimmte C++-Klassenkonstruktion zu verbieten, als den Konstruktor als privat zu deklarieren?
-
26-10-2019 - |
Frage
Angenommen, ich habe mit einigen einen Kurs const-Referenz Mitgliedsvariable und ich möchte eine bestimmte Art der Konstruktion verbieten.Also würde ich den entsprechenden Konstruktor deklarieren Privat.Natürlich muss ein Konstruktor alles initialisieren const-Referenz Mitgliedsvariablen der Klasse.Dies führt jedoch zu seltsam aussehendem Code:
class A {
};
class B {
B(const A& a): host(a) {}
private:
B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
Gibt es eine andere Möglichkeit, einen bestimmten Bautyp zu verbieten, als den Konstruktor als privat zu deklarieren?Ich möchte nicht, dass der Compiler einen Konstruktor für mich schreibt.
Lösung
Definieren Sie das einfach nicht:
B():host(A()) {} // This is ugly and not needed !!
Das heißt, Folgendes sollte das tun, was Sie tun möchten:
class B {
B(const A& a): host(a) {}
private:
//B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
Die Idee besteht darin, dass, wenn Sie einen Konstruktor definiert haben, der Parameter akzeptiert, der Standardkonstruktor nicht vom Compiler generiert wird.Das heißt, Instanzen der oben genannten Klasse kann nicht Sei Standard erstellt!
B b1; //error - needs default constructor which doesn't exist!
B b2(a); //ok - only way to create an instance!
C++11-Lösung
In C++11 können Sie den Compiler explizit anweisen, keinen bestimmten Konstruktor zu generieren:
struct B
{
B(const A &a) {}
B() = delete; //disable
};
Nicht nur das.Es steckt noch mehr dahinter, wie unten erklärt:
Nun der interessante Teil
Sie können Konstruktoren auch selektiv deaktivieren ausgewählt Typen was macht delete
interessanter.Bedenken Sie,
struct A
{
A (int) {}
};
Objekte dieser Klasse können nicht nur mit erstellt werden int
Argument, sondern jeder Typ, der implizit konvertiert wird int
.Zum Beispiel,
A a1(10); //ok
A a2('x'); //ok - char can convert to int implicitly
B b;
A a3(b); //ok - assume b provides user-defined conversion to int
Nehmen wir nun an, aus welchem Grund auch immer, ich möchte die Benutzer der Klasse nicht haben A
Objekte damit erstellen char
oder class B
, was glücklicherweise oder leider möglich ist implizit konvertieren zu int
, dann können Sie sie wie folgt deaktivieren:
struct A
{
A(int) {}
A(char) = delete; //disable
A(const B&) = delete; //disable
};
Jetzt geht es los:
A a1(10); //ok
A a2('x'); //error
B b;
A a3(b); //error - assume (even if) b provides user-defined conversion to int
Online-Demo: http://ideone.com/EQl5R
Die Fehlermeldungen sind sehr klar:
prog.cpp:9:5:Fehler:gelöschte Funktion 'A::A(char)'
prog.cpp:10:5:Fehler:gelöschte Funktion 'A::A(const B&)'
Andere Tipps
Lass es einfach aus. Sobald Sie einen benutzerdefinierten Konstruktor zur Verfügung stellen, wird kein anderer Konstruktor automatisch generiert (mit Ausnahme eines Kopierkonstruktors).
Wenn Sie verbieten wollen irgendein Konstruktion - endet mit einer Klasse, die nur statische Mitglieder hat - Sie können einfach erklären der Konstruktor als privat und nicht definieren es. Eine solche Klasse ist in C ++ sehr selten nützlich (da Sie keine Instanzen davon erstellen können); Der einzige Zweck, den ich mir vorstellen kann, ist die Implementierung von Merkmalklassen:
template <typename T>
struct type_to_color {
static char const* value() { return "blue"; }
private:
type_to_color();
};
template <>
struct type_to_color<int> {
// Integers are red!
static char const* value() { return "red"; }
private:
type_to_color();
}
char const* char_color = type_to_color<char>::value();
char const* int_color = type_to_color<int>::value();
Dies ist jedoch äußerst ungewöhnlich: Merkmalklassen sind in C ++ reichlich vorhanden, aber sie erklären ihre Konstrukteure nie als private
, Es wird nur angenommen, dass jeder weiß, dass er sie nicht instanziiert.
Ich werde die C ++ 11 -Lösung veröffentlichen: löschen der Konstruktor.
class B {
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};
Wie Konrad Rudolph Sayd: Sobald Sie einen benutzerdefinierten Konstruktor zur Verfügung stellen, wird kein anderer Konstruktor automatisch generiert (mit Ausnahme eines Kopierkonstruktors).
Daher sind andere Optionen:
Deklarieren Sie den Konstruktor privat (damit Sie nicht von Ihrer Klasse erben können), geben aber keine Definition an:
class B {
public:
B(const A& a): host(a) {}
private:
B(); // not implemented!
const A& host;
};
Oder in C ++ 11, wie R. Martinho Fernandes sagt:
class B {
public:
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};