Duplicata possible:
Évitez d'avoir une méthode d'initialisation
Je veux déterminer quand faire l'initialisation non triviale d'une classe. Je vois deux fois faire l'initialisation: constructeur et autre méthode. Je veux savoir quand les utiliser.
Le constructeur fait l'initialisation
MyClass::MyClass(Data const& data) : m_data()
{
// does non-trivial initialization here
}
MyClass::~MyClass()
{
// cleans up here
}
Différer l'initialisation à une méthode d'initialisation
MyClass::MyClass() : m_data()
{}
MyClass::Initialize(Data const& data)
{
// does non-trivial initialization here
}
MyClass::~MyClass()
{
// cleans up here
}
Utilisez toujours le constructeur, sauf s'il existe une bonne raison de ne pas le faire. C'est "The C++ Way" (tm).
Concernant vos points à considérer:
Les constructeurs sont toujours plus ou tout aussi efficaces que d'avoir du code à l'extérieur dans des fonctions init () distinctes.
Les constructeurs sont généralement plus faciles à utiliser pour les autres développeurs. Sans regarder votre source ou vos documents, je m'attendrais à ce que new YourClass(stuff)
fonctionne. Le fait d'appeler une yourClass->init(stuff)
par la suite n'est pas imposé par le compilateur et c'est une erreur facile à faire.
Selon le numéro 2 - de nombreuses mises en garde concernant les constructeurs sont formulées pour vous par les compilateurs, en termes d'ordre d'initialisation, etc.
Idéalement, utilisez simplement un constructeur. C'est généralement une mauvaise chose lorsqu'un constructeur retourne un objet pas tout à fait utilisable.
Cependant, comme les gens l'ont souligné, vous avez souvent des situations où les données nécessaires pour initialiser complètement un objet ne sont pas disponibles au moment de la construction. Vous pouvez gérer une situation comme celle-ci en utilisant le modèle de générateur.
Disons que vous avez une classe Foo
, qui nécessite une initialisation non triviale. Vous créez une classe FooBuilder
, qui stocke simplement toutes les données nécessaires pour initialiser un objet de Foo
. FooBuilder
aurait une fonction membre (méthode aka) Foo *build()
ou peut-être Foo build()
, que vous appelleriez lorsque toutes les données seront collectées. Il pourrait également avoir des paramètres pour divers éléments qui doivent être transmis au constructeur de Foo, et il pourrait fournir des paramètres par défaut pour certains d'entre eux.
Cela résout le problème de "l'initialisation tardive" sans nécessiter une fonction membre initialize()
. Par exemple, si vous devez créer un tableau d'objets Foo
avant de disposer de toutes les informations nécessaires pour les initialiser, vous devez plutôt créer un tableau de FooBuilder
s. Ensuite, vous appelez les setters appropriés sur les générateurs à mesure que les données deviennent disponibles. Enfin, lorsque toutes les données sont stockées en toute sécurité dans les générateurs, vous créez un tableau de Foo
, en appelant build()
sur chaque générateur.
Une option que personne ne semblait aborder est au lieu de construire puis Init, en utilisant un constructeur privé et une fonction Initialize statique. En effet, c'est une technique puissante car en théorie la fonction d'initialisation statique peut construire différentes sous-classes en fonction du contexte.
Je choisirais l'un ou l'autre. Comme d'autres l'ont mentionné, le fait d'avoir un constructeur suivi d'un appel Initialize est sujet aux erreurs.
Utilisez une Init()
lorsque vous devez fournir à votre objet des paramètres que vous ne savez pas/ne pouvez pas savoir lorsque vous créez l'objet.
Vous pouvez également l'utiliser si la fonctionnalité de votre classe peut prendre du temps à construire, par exemple l'initialisation de la lecture d'un fichier vidéo implique l'analyse de fichiers, la correspondance et le chargement de codecs vidéo, la correspondance et le chargement de codecs audio, l'acquisition de ressources. Dans ce cas, vous pouvez diviser la création en étapes asynchrones afin que l'application de votre utilisateur reste réactif et permette également d'annuler l'opération.