En C++, vous pouvez utiliser une liste d'initialisation pour initialiser les champs de la classe avant que le constructeur ne commence à s'exécuter. Par exemple:
Foo::Foo(string s, double d, int n) : name(s), weight(d), age(n) {
// Empty; already handled!
}
Je suis curieux de savoir pourquoi Java n'a pas de fonctionnalité similaire. Selon Core Java: Volume 1:
C++ utilise cette syntaxe spéciale pour appeler les constructeurs de champs. En Java, il n'est pas nécessaire car les objets n'ont pas de sous-objets, seulement des pointeurs vers d'autres objets.
Voici mes questions:
Qu'entendent-ils par "parce que les objets n'ont pas de sous-objets?" Je ne comprends pas ce qu'est un sous-objet (j'ai essayé de le rechercher); signifient-ils une instanciation d'une sous-classe qui prolonge une super-classe?
Quant à savoir pourquoi Java n'a pas de listes d'initialisation comme C++, je suppose que la raison en est que tous les champs sont déjà initialisés par défaut dans Java et aussi parce que Java utilise le mot clé super
pour appeler le constructeur de classe super (ou base dans le jargon C++). Est-ce correct?
En C++, les listes d'initialisation sont nécessaires en raison de quelques fonctionnalités de langage qui ne sont pas présentes dans Java ou fonctionnent différemment en Java:
const
: En C++, vous pouvez définir des champs marqués const
qui ne peuvent pas être affectés et doivent être initialisés dans la liste d'initialisation. Java possède des champs final
, mais vous pouvez les affecter à final
champs dans le corps d'un constructeur. En C++, affecter à un const
dans le constructeur est illégal.
Références: En C++, les références (par opposition aux pointeurs) doivent être initialisées pour se lier à un objet. Il est illégal de créer une référence sans initialiseur. En C++, la façon dont vous spécifiez cela est avec la liste d'initialisation, car si vous deviez faire référence à la référence dans le corps du constructeur sans l'initialiser au préalable, vous utiliseriez une référence non initialisée. En Java, les références d'objet se comportent comme des pointeurs C++ et peuvent être affectées à une fois créées. Ils sont simplement par défaut à null
sinon.
Sous-objets directs. En C++, un objet peut contenir un objet directement sous forme de champs, alors qu'en Java les objets ne peuvent contenir que les références à ces objets C'est-à-dire qu'en C++, si vous déclarez un objet qui a un string
en tant que membre, l'espace de stockage de cette chaîne est construit directement dans l'espace de l'objet lui-même, tandis qu'en Java vous obtenez juste de l'espace pour une référence à un autre String
objet stocké ailleurs. Par conséquent, C++ doit vous fournir un moyen de donner à ces sous-objets les valeurs initiales, sinon ils resteraient simplement non initialisé. Par défaut, il utilise le constructeur par défaut pour ces types, mais si vous souhaitez utiliser un constructeur différent ou qu'aucun constructeur par défaut n'est disponible, la liste d'initialisation vous donne un moyen de contourner cela. En Java, vous n'avez pas à vous soucier de cela parce que les références seront par défaut à null
, et vous pouvez ensuite les affecter pour faire référence aux objets auxquels vous voulez qu'ils se réfèrent. Si vous souhaitez utiliser un constructeur non par défaut, alors vous d on n'a pas besoin de syntaxe spéciale pour cela; il suffit de définir la référence à un nouvel objet initialisé via le constructeur approprié.
Dans les quelques cas où Java peut vouloir des listes d'initialisation (par exemple, pour appeler des constructeurs de superclasse ou donner des valeurs par défaut à ses champs), cela est géré par le biais de deux autres fonctionnalités de langage: le super
mot-clé pour appeler les constructeurs de superclasses, et le fait que Java peuvent donner à leurs champs des valeurs par défaut au point où ils sont déclarés. Puisque C++ a un héritage multiple, il n'en reste qu'un super
mot-clé ne ferait pas référence sans ambiguïté à une seule classe de base, et avant C++ 11, C++ ne supportait pas les initialiseurs par défaut dans une classe et devait s'appuyer sur des listes d'initialiseurs.
J'espère que cela t'aides!
C++
Il y a une différence entre
ClassType t(initialization arguments);
et
ClassType * pt;
Ce dernier n'a pas besoin d'être initialisé (défini sur NULL). Le premier le fait. Considérez-le comme un entier. Vous ne pouvez pas avoir un int sans valeur, MAIS vous pouvez avoir un pointeur int sans valeur.
Donc, quand vous avez:
class ClassType
{
OtherClass value;
OtherClass * reference;
};
Puis la déclaration:
ClassType object;
crée automatiquement une instance de OtherClass
dans value
. Par conséquent, si OtherClass
a une initialisation, elle doit être effectuée dans le constructeur ClassType
. Cependant, reference
n'est qu'un pointeur (adresse en mémoire) et peut rester non initialisé. Si vous voulez une instance de OtherClass
vous devez utiliser
object.reference = new OtherClass(initialization arguments);
Java
Il n'y a que
class ClassType
{
OtherClass reference;
}
Cela équivaut à un pointeur en C++. Dans ce cas, lorsque vous le faites:
ClassType object = new ClassType();
Vous ne créez pas automatiquement une instance de OtherClass
. Par conséquent, vous n'avez rien à initialiser dans le constructeur, sauf si vous le souhaitez. Lorsque vous voulez un objet de OtherClass
vous pouvez utiliser
object.reference = new OtherClass();
Parce que Java n'en a pas besoin pour permettre l'initialisation des champs dont le type n'a pas de valeur nulle.
En C++
class C {
D d;
}
sans initialiseur de membre pour d
, D::D()
sera appelée, ce qui rend impossible l'initialisation du champ s'il n'y a pas de type zéro pour D
. Cela peut se produire lorsque D::D()
est explicitement déclaré private
.
En Java, il existe un valeur nulle connu pour tous les types de référence, null
, donc un champ peut toujours être initialisé.
Java fait également beaucoup de travail pour s'assurer * que tous les champs final
sont initialisés avant la première utilisation et avant la fin du constructeur, donc tandis que Java a une exigence comme C++ _ const
exigence d'initialisation du champ, il surcharge simplement this.fieldName = <expression>
dans le corps du constructeur pour signifier l'initialisation du champ.