Comment initialiser les membres statiques privés EN C++?
Quelle est la meilleure façon d'initialiser un membre de données statique privé en C++? J'ai essayé cela dans mon fichier d'en-tête, mais cela me donne des erreurs de linker bizarres:
class foo
{
private:
static int i;
};
int foo::i = 0;
je suppose que c'est parce que je ne peux pas initialiser un membre privé de l'extérieur de la classe. Quelle est donc la meilleure façon de le faire?
17 réponses
la déclaration de classe doit être dans le fichier d'en-tête (ou dans le fichier source s'il n'est pas partagé).
Fichier: foo.h
class foo
{
private:
static int i;
};
Mais l'initialisation doit être dans le fichier source.
Fichier: foo.cpp
int foo::i = 0;
Si l'initialisation est dans le fichier d'en-tête, puis chaque fichier qui contient le fichier d'en-tête aura une définition du membre statique. Ainsi pendant la phase de lien vous obtiendrez linker les erreurs comme le code pour initialiser la variable seront définies dans les fichiers source multiples.
Note: Matt Curtis: souligne que C++ permet la simplification de ce qui précède si la variable membre statique est de type constant (par ex. int
, bool
, char
). Vous pouvez alors déclarer et initialiser la variable member directement à l'intérieur de la déclaration de classe dans le fichier d'en-tête:
class foo
{
private:
static int const i = 42;
};
pour une variable :
foo.h:
class foo
{
private:
static int i;
};
foo.cpp:
int foo::i = 0;
C'est parce qu'il ne peut être une instance de foo::i
dans votre programme. C'est un peu l'équivalent de extern int i
dans un fichier d'en-tête et int i
dans un fichier source.
Pour un constante vous pouvez mettre la valeur directement dans la déclaration de classe:
class foo
{
private:
static int i;
const static int a = 42;
};
pour les futurs téléspectateurs de cette question, je tiens à souligner que vous devriez éviter ce que monkey0506 suggère .
Les fichiers D'en-têtesont pour les déclarations.
Les fichiers D'en-tête sont compilés une fois pour chaque fichier .cpp
qui les #includes
directement ou indirectement, et le code en dehors de toute fonction est lancé à l'initialisation du programme, avant main()
.
en mettant: foo::i = VALUE;
dans l'en-tête, foo:i
se verra attribuer la valeur VALUE
(quelle qu'elle soit) pour chaque fichier .cpp
, et ces assignations se feront dans un ordre indéterminé (déterminé par le linker) avant que main()
ne soit lancé.
et si #define VALUE
était un autre numéro dans l'un de nos fichiers .cpp
? Il compilera très bien et nous n'aurons aucun moyen de savoir qui gagne jusqu'à ce que nous exécutions le programme.
jamais mis exécuté code dans un en-tête pour la même raison que vous jamais #include
un .cpp
fichier.
inclure les gardes (qui je suis d'accord que vous devriez toujours utiliser) de vous protéger de quelque chose de différent: le même en-tête étant indirectement #include
d plusieurs fois lors de la compilation d'un seul .cpp
fichier
avec un compilateur Microsoft[1], Les variables statiques qui ne sont pas int
- comme peuvent également être définies dans un fichier d'en-tête, mais en dehors de la déclaration de classe, en utilisant le Microsoft spécifique __declspec(selectany)
.
class A
{
static B b;
}
__declspec(selectany) A::b;
notez que je ne dis pas que c'est bon, je dis juste que c'est faisable.
[1] de nos jours, plus de compilateurs que MSC prennent en charge __declspec(selectany)
- au moins gcc et clang. Peut-être même plus.
int foo::i = 0;
est la syntaxe correcte pour initialiser la variable, mais elle doit aller dans le fichier source (.rpc) plutôt que dans l'en-tête.
Parce que c'est une variable statique le compilateur a besoin de créer qu'une seule copie. Vous devez avoir une ligne "int foo:j'ai" quelque part dans votre code pour indiquer au compilateur où le mettre sinon vous aurez une erreur de lien. Si c'est dans un en-tête, vous obtiendrez une copie de chaque fichier qui contient l'en-tête, afin de vous multipliez défini les erreurs de symboles du linker.
Je n'ai pas assez de rapport ici pour ajouter ceci comme commentaire, mais IMO c'est du bon style d'écrire vos en-têtes avec #include guards de toute façon, ce qui comme noté par Paranaix il y a quelques heures empêcherait une erreur de définition multiple. À moins que vous n'utilisiez déjà un fichier distinct pour le RPC, il n'est pas nécessaire d'en utiliser un pour initialiser les participants non intégraux.
#ifndef FOO_H
#define FOO_H
#include "bar.h"
class foo
{
private:
static bar i;
};
bar foo::i = VALUE;
#endif
Je ne vois pas la nécessité d'utiliser un fichier distinct pour le RPC. Bien sûr, vous pouvez, mais il n'y a pas de raison technique pour laquelle vous devriez avoir.
si vous voulez initialiser un type de composé (F. E. string) vous pouvez faire quelque chose comme ça:
class SomeClass {
static std::list<string> _list;
public:
static const std::list<string>& getList() {
struct Initializer {
Initializer() {
// Here you may want to put mutex
_list.push_back("FIRST");
_list.push_back("SECOND");
....
}
}
static Initializer ListInitializationGuard;
return _list;
}
};
comme le ListInitializationGuard
est une variable statique à l'intérieur de SomeClass::getList()
méthode, il sera construit qu'une seule fois, ce qui signifie que le constructeur est appelé une fois. Cela va initialize _list
variable à la valeur que vous avez besoin. Tout appel ultérieur à getList
retournera simplement l'objet _list
déjà initialisé.
bien sûr, vous devez accédez à _list
object toujours en appelant la méthode getList()
.
depuis C++17, les membres statiques peuvent être définis dans l'en-tête avec le mot-clé inline .
http://en.cppreference.com/w/cpp/language/static
" un élément de données statique peut être déclaré en ligne. Un membre de données statiques en ligne peut être défini dans la définition de classe et peut spécifier un initialiseur de membre par défaut. Il n'a pas besoin d'une définition de classe:"
struct X
{
inline static int n = 1;
};
vous pouvez également inclure l'affectation dans le fichier d'en-tête si vous utilisez des en-têtes de garde. J'ai utilisé cette technique pour une bibliothèque C++ que j'ai créé. Une autre façon d'obtenir le même résultat consiste à utiliser des méthodes statiques. Exemple...
class Foo
{
public:
int GetMyStatic() const
{
return *MyStatic();
}
private:
static int* MyStatic()
{
static int mStatic = 0;
return &mStatic;
}
}
le code ci-dessus a le" bonus " de ne pas exiger un fichier RPC/source. Encore une fois, une méthode que j'utilise pour mes bibliothèques C++.
Je suis L'idée de Karl. Je l'aime et maintenant je l'utilise ainsi. J'ai un peu modifié la notation et ajouté quelques fonctionnalités
#include <stdio.h>
class Foo
{
public:
int GetMyStaticValue () const { return MyStatic(); }
int & GetMyStaticVar () { return MyStatic(); }
static bool isMyStatic (int & num) { return & num == & MyStatic(); }
private:
static int & MyStatic ()
{
static int mStatic = 7;
return mStatic;
}
};
int main (int, char **)
{
Foo obj;
printf ("mystatic value %d\n", obj.GetMyStaticValue());
obj.GetMyStaticVar () = 3;
printf ("mystatic value %d\n", obj.GetMyStaticValue());
int valMyS = obj.GetMyStaticVar ();
int & iPtr1 = obj.GetMyStaticVar ();
int & iPtr2 = valMyS;
printf ("is my static %d %d\n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2));
}
ce sorties
mystatic value 7
mystatic value 3
is my static 1 0
travaille aussi dans privateStatic.fichier cpp :
#include <iostream>
using namespace std;
class A
{
private:
static int v;
};
int A::v = 10; // possible initializing
int main()
{
A a;
//cout << A::v << endl; // no access because of private scope
return 0;
}
// g++ privateStatic.cpp -o privateStatic && ./privateStatic
Qu'en est-il de la méthode set_default()
?
class foo
{
public:
static void set_default(int);
private:
static int i;
};
void foo::set_default(int x) {
i = x;
}
nous n'aurions qu'à utiliser la méthode set_default(int x)
et notre variable static
serait initialisée.
ce ne serait pas en désaccord avec le reste des commentaires, en fait, il suit le même principe d'initialisation de la variable dans une portée globale, mais en utilisant cette méthode, nous le rendons explicite (et facile à voir-comprendre) au lieu d'avoir la définition de la variable accroché là.
modèle de constructeur statique qui fonctionne pour des objets multiples
un idiome a été proposé à: https://stackoverflow.com/a/27088552/895245 Mais voici une version plus propre qui ne nécessite pas de créer une nouvelle méthode par membre, et un exemple exécutable:
#include <cassert>
#include <vector>
// Normally on the .hpp file.
class MyClass {
public:
static std::vector<int> v, v2;
static struct _StaticConstructor {
_StaticConstructor() {
v.push_back(1);
v.push_back(2);
v2.push_back(3);
v2.push_back(4);
}
} _staticConstructor;
};
// Normally on the .cpp file.
std::vector<int> MyClass::v;
std::vector<int> MyClass::v2;
// Must come after every static member.
MyClass::_StaticConstructor MyClass::_staticConstructor;
int main() {
assert(MyClass::v[0] == 1);
assert(MyClass::v[1] == 2);
assert(MyClass::v2[0] == 3);
assert(MyClass::v2[1] == 4);
}
Voir aussi: constructeurs statiques en C++? J'ai besoin d'initialiser privé objets statiques
Testé avec g++ -std=c++11 -Wall -Wextra
, GCC 7.2, Ubuntu 17.10.
je voulais juste mentionner quelque chose d'un peu étrange pour moi quand je l'ai rencontré pour la première fois.
j'avais besoin d'initialiser un privé donnée membre statique dans une classe de modèle.
dans le .h ou .hpp, il ressemble à quelque chose comme ceci pour initialiser un membre de données statiques d'une classe de modèle:
template<typename T>
Type ClassName<T>::dataMemberName = initialValue;
le problème de connexion que vous avez rencontré est probablement causé par:
- Offrant à la fois classe et membre statique définition dans le fichier d'en-tête,
- incluant cet en-tête dans deux fichiers source ou plus.
C'est un problème courant pour ceux qui commencent avec C++. Le membre de classe statique doit être initialisé dans une seule unité de traduction, c'est-à-dire dans un fichier source unique.
malheureusement, la statique des membres du groupe doit être initialisé à l'extérieur du corps de la classe. Cela complique l'écriture du code d'en-tête seulement, et, par conséquent, j'utilise une approche tout à fait différente. Vous pouvez fournir votre objet statique par la fonction de classe statique ou non-statique par exemple:
class Foo
{
// int& getObjectInstance() const {
static int& getObjectInstance() {
static int object;
return object;
}
void func() {
int &object = getValueInstance();
object += 5;
}
};
est-ce que cela sert votre but?
//header file
struct MyStruct {
public:
const std::unordered_map<std::string, uint32_t> str_to_int{
{ "a", 1 },
{ "b", 2 },
...
{ "z", 26 }
};
const std::unordered_map<int , std::string> int_to_str{
{ 1, "a" },
{ 2, "b" },
...
{ 26, "z" }
};
std::string some_string = "justanotherstring";
uint32_t some_int = 42;
static MyStruct & Singleton() {
static MyStruct instance;
return instance;
}
private:
MyStruct() {};
};
//Usage in cpp file
int main(){
std::cout<<MyStruct::Singleton().some_string<<std::endl;
std::cout<<MyStruct::Singleton().some_int<<std::endl;
return 0;
}
une façon" old-school "de définir les constantes est de les remplacer par un enum
:
class foo
{
private:
enum {i = 0}; // default type = int
enum: int64_t {HUGE = 1000000000000}; // may specify another type
};
de cette façon ne nécessite pas de fournir une définition, et évite de faire la constante lvalue , ce qui peut vous épargner quelques maux de tête, par exemple lorsque vous accidentellement ODR-utilisation it.