2009-04-23 5 views
11

J'ai un membre de classe statique qui est un certain conteneur, commePeupler un conteneur de membre statique dans C++

(.h)

class Foo 
{ 
    ... 
private: 
    static list<string> s_List; 
} 

je dois remplir la liste avec un certain nombre de valeurs spécifiques. En fait, il devrait également être const, mais cela pourrait compliquer le problème plus loin. Toutes les fonctions membres de la classe sont statiques, donc l'initialiser dans un constructeur n'a pas de sens.

Répondre

15

une solution commune est de faire quelque chose comme ceci:

// header 
class Foo 
{ 
... 
private: 
    static list<string> s_List; 
} 

// cpp 
list<string> init() 
{ 
    list<string> tmp; 
    ... fill tmp with strings 

    return tmp; 
} 

list<string> Foo::s_List(init()); 

l'autre méthode est comme Neil Butterworth a suggéré.

+1

Je suppose que la vôtre est une solution plus polyvalente, car un membre privé peut être initialisé sans problème ... et est-ce que s_List peut être const? Ensuite, init() doit également renvoyer la liste de const . Droite? – Maleev

+0

Aussi, du point de vue de la performance: ne vaudrait-il pas mieux renvoyer une référence à tmp, car il sera copié de toute façon par le constructeur de copie implicite (ou opérateur d'affectation, corrigez-moi) dans la dernière ligne? Alors nous ne serons pas coppies deux fois. Des sous-titres que je n'ai pas comptés? – Maleev

+0

oui, vous pouvez le changer en une liste const <>. Cependant, vous ne pouvez pas renvoyer de référence car "tmp" est une variable locale et mourra une fois que vous aurez quitté init(). –

0

Les façons dont j'ai (l'auteur de la question) ont vainement tenté de le faire.


J'ai essayé de faire grincer comme (en foo.cpp):

list<string> Foo::s_List = list<string>(); 
Foo::s_List.insert("apple"); 
Foo::s_List.insert("bannana"); 
Foo::s_List.insert("grapes"); 

Mais cela donne une erreur de compilation.


Alors je pensais de faire une méthode initialize() et de l'appeler directement à partir du code

void Foo::Initialize() 
{ 
    s_List.insert("rats"); 
    s_List.insert("cats"); 
} 

Foo::Initialize(); 

// erreur: compilateur considère comme un redefenition de la méthode, pas un appel.


La seule idée viable à gauche (havent encore essayé) serait de vérifier si la liste est vide dans chaque méthode qui l'utilise, et si c'est le cas, appelez initialize(). Mais c'est moche!

+0

Initialiser doit être une fonction statique. Désolé pour la question, mais essayez-vous de faire des appels de fonction * en dehors de * fonctions ou méthodes? – Emiliano

+0

Oui, en fait j'étais :) – Maleev

0

une solution possible serait d'utiliser une méthode d'accès qui vérifie si elle est initialisée, et ce, si ce n'est pas le cas.

8

Une autre alternative est de créer une simple classe initialiseur:

list <string> Foo::s_List; 

struct Init { 
    Init() { 
     Foo::s_List.insert("apple"); 
     Foo::s_List.insert("bannana"); 
     Foo::s_List.insert("grapes"); 
    } 
}; 

static Init doInit; 

Notez que, comme la liste est privée, il faudra probablement vous Init faire un ami de Foo. Il est également souvent pratique de faire en sorte que ces classes soient contenues par la classe qu'elles initialisent. Cependant, je viens de relire votre question et une autre pensée se produit - si la liste est const, vous ne la changerez probablement pas, dans ce cas un simple tableau de chaînes, initialisé avec les chaînes dans l'ordre trié peut être une meilleure solution. Il sera certainement plus rapide de rechercher (en utilisant std :: binary_search) qu'une liste, et peut bien sûr être facilement initialisé.

+0

+1, battu moi :) –

+2

Bonne idée. Le fonctionnement est garanti, car les membres statiques et globaux sont initialisés dans l'ordre dans chaque fichier source. Puisque la liste est privée dans la question originale, il faudra une déclaration d'ami. –

+0

Ça a l'air génial. Merci – Maleev

1

Cela dépend des valeurs que vous devez mettre dans cette liste. Sont-ils statiques ou nécessitent-ils une forme de calcul?

Si elles sont statiques, vous pouvez le faire:

namespace { 
    const char* const initVals[] = { "A", "B", "C" }; 
} 

list<string> Foo::s_list(initVals, initVals + 3); 
4

Si votre compilateur prend en charge C++ 0x, c'est en fait trivial à accomplir.

#include <iostream> 
#include <list> 

class Foo 
{ 
public: 
    static std::list<std::string> s_List; 
}; 

std::list<std::string> Foo::s_List = {"hello", "world", "asdf", "qwerty"}; 

int main() 
{ 
    for(const std::string& str : Foo::s_List) 
     std::cout << str << std::endl; 

    return 0; 
} 

Ceci fonctionne à la fois pour les membres statiques const et non-const. J'ai testé cet extrait avec clang-4.2, gcc-4.7, gcc-4.6 et gcc-4.5. Gcc-4.5 ne prend pas en charge la syntaxe mise à jour pour la syntaxe, donc vous devrez utiliser une boucle traditionnelle pour avec des itérateurs. En outre, n'oubliez pas de passer l'indicateur -std = C++ 0x au compilateur. Je suis raisonnablement confiant que Visual Studio supporte cela aussi, mais je ne sais pas avec certitude et je ne sais pas quelles versions.