12
votes

Initialisation complexe des champs de const

Considérez une classe comme celle-ci:

class MyReferenceClass
{
public:
    MyReferenceClass();
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;
private:
    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
}


1 commentaires

Si c'est possible, pouvez-vous indiquer comment ComputeImportantConstants est implémenté? Est-ce assez long? Comment les trois constantes interagissent-elles, quels autres facteurs sont impliqués?


8 Réponses :


10
votes

Pourquoi ne pouvez-vous pas faire: xxx

comme ça et que la fonction de calcul est de la fonction de calcul dans une fonction d'usine?


5 commentaires

Une idée d'amélioration: faire une variable statique dans ComputeImportantConstants () et renvoyer cette variable une fois que tout est calculé. De cette façon, les appels ultérieurs de ComputeImpportantConstants ne déclenchent pas de calcul supplémentaire.


C ++ Nitpick: retourner un const double d'une fonction n'a pas beaucoup de sens. Cela ne rend que la vie plus difficile que nécessaire pour l'appelant sans améliorer la sécurité. Les variables de membre sont renvoyées par la valeur, après tout.


Il n'y a rien de mal avec une structure n'ayant aucune méthode et que des données de maintien (types de pod.) D'autre part, il est souvent beaucoup plus facile d'affecter les changements dans un programme si ce qui est accessible est caché derrière un appel de fonction. La plupart des gens vont préférer l'encapsulation à cause de ce dernier.


Le Nitpick est faux - il n'y a pas de const double renvoyé, seul un double est renvoyé par une méthode const , c'est-à-dire une méthode qui garantit de ne pas changer le contenu.


@Chris: Si vous regardez l'historique des changements, vous verrez qu'environ il y a environ deux ans, était un const double. ;-)



5
votes

Tout d'abord - vous pouvez faire le mal: jeter const dans ComputeImportantConstants () et placer les valeurs là-bas. Ne le faites pas cependant, car alors vous mentez au compilateur et il tentera de trouver le moyen le plus méchant de rembourser.

second:

faire quelque chose comme ça: < Pré> xxx

Vous pouvez toujours améliorer cette classe en faisant une sorte de singleton ou donc (puisque vous voulez que le calcul soit effectué une seule fois).


0 commentaires

3
votes

Vous pouvez déplacer les champs const code> à une classe de base, puis transmettez une classe d'emballage pour les initialiser:

class MyBase
{
protected:
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;

    struct Initializer
    {
        double d1;
        double d2;
        double d3;
    };

    MyBase(Initializer const& i):
        ImportantConstant1(i.d1),ImportantConstant2(i.d2),ImportantConstant3(i.d3)
    {}
};

class MyReferenceClass:
    private MyBase
{
public:
    using MyBase::ImportantConstant1;
    using MyBase::ImportantConstant2;
    using MyBase::ImportantConstant3;
    MyReferenceClass():
        MyBase(makeInitializer())
    {}

private:
    MyBase::Initializer makeInitializer()
    {
        MyBase::Initializer i;
        ComputeImportantConstants(&i.d1,&i.d2,&i.d3);
        return i;
    }

    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
};


1 commentaires

Fonctionne bien; Il suffit de passer à l'héritage de la mybase publique et de rendre les deux terrains de const public public. De plus, dans le code réel, les trois valeurs sont déjà transmises dans une structure afin que je n'ai pas besoin d'une structure supplémentaire d'initialisateur. (J'aurais vraiment dû écrire ma question en utilisant cette structure ...)



2
votes

Le seul moyen d'initialiser les champs de const que j'ai trouvé est d'utiliser des listes d'initialistes, mais il ne semble pas possible de passer les résultats d'un calcul multi-sorties dans une telle liste.

c'est vrai; Cependant, vous pouvez initialiser un seul membre - qui est une structure de constantes. Voir ci-dessous.

J'ai également envisagé de faire des informations importateurs1..3 des champs ou des appels de fonction non constitutifs, mais les deux semblent inférieurs.

Je ne pense pas que les fonctions getter seraient inférieures. Le compilateur les ferait probablement les aligner. Considérez ceci: xxx

puisque m_constants ainsi que tous ses champs sont constants, les valeurs ne peuvent pas être modifiées par d'autres méthodes membres - juste dans le code vous avez esquissé dans votre question. Une initialisation peut être utilisé ici depuis que nous initialisons une seule valeur: une structure.

L'accès aux constantes est (très probablement) d'être aussi efficace qu'auparavant: le suggère de faire connaître les fonctions et le compilateur est assez susceptible de le faire. étant donné à quel point les getters sont petits.


0 commentaires

1
votes

Il suffit de scinder la chose dans la pièce qui est simple à initialiser et à la partie complexe et à initialiser la pièce complexe via Copier Constructeur:

// here's the part with the consts: 
struct ComplexPart
{
    const double a,b,c; 
    ComplexPart(double _a, double _b, double _c) {}
};
// here's the expensive calc function:
void calc(double *a,double *b,double *c);

// and this is a helper which returns an initialized ComplexPart from the computation:
ComplexPart calc2()
{
    double *a,*b,*c;
    calc(&a,&b,&b);
    return ComplexPart(a,b,c);
}
// put everything together:    
struct MyReferenceClass : public ComplexPart
{
    MyReferenceClass() : ComplexPart(calc2()) {}
};


0 commentaires

1
votes

Qu'en est-il de quelque chose comme ça: xxx


0 commentaires

1
votes

Aucune de la réponse ci-dessus semblait faire attention à un détail: statique code> est mentionné ici, de sorte que ces constantes semblent être indépendantes de l'instance réelle de la classe.

En d'autres termes: Ce sont des constantes mondiales. Comme vous l'avez deviné, la présence du mot-clé const code> est importante ici, en raison des optimisations du compilateur s'appliquera. P>

Quoi qu'il en soit, l'idée est d'utiliser une structure d'assistance. P>

// foo.h
class Foo
{
public:
  static double const m1;
  static double const m2;
  static double const m3;
};

// foo.cpp
struct Helper
{
  double m1, m2, m3;
  Helper() { complexInit(m1, m2, m3); }
} gHelper;

double const Foo::m1 = gHelper.m1;
double const Foo::m2 = gHelper.m2;
double const Foo::m3 = gHelper.m3;


0 commentaires

2
votes

Pour modifier la réponse acceptée, veuillez noter qu'à partir de C ++ 11, vous pouvez faire des trucs très soignés. Par exemple, votre problème d'origine peut être résolu avec une délégation de la Lambda et de la construction comme suit: xxx

ou mieux encore, déplacez le code d'initialisation de ComputeImportantConstants dans la Lambda Le constructeur, si possible.

En pratique, utiliser des appels Lambda pour initialiser les membres constants est un tour très pratique, notamment parce que vous pouvez également lier et / ou passer des arguments à la Lambda. Et l'utilisation de la délégation de la construction aide à faciliter l'initialisation des membres qui peut être mieux initialisée ensemble ou pourrait dépendre de l'autre.

Cependant, faites preuve de prudence supplémentaire lors de l'utilisation de la délégation de la construction, car l'ordre d'initialisation des arguments de fonction pour une fonction L'appel (ou un appel de constructeur) est indéfini et on pourrait finaliser d'initialiser les choses dans un ordre incorrect, ou de manière à conduire à des fuites de ressources si quelque chose échoue ou jette une exception.


0 commentaires