Jak inicializovat private static členy v C ++?

hlasů
388

Jaký je nejlepší způsob, jak inicializovat soukromé, statický datový člen v C ++? Zkoušel jsem to ve svém záhlaví souboru, ale to mi dává divné chyby linker:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

Hádám, že je to proto, že nemohu inicializovat soukromé člen z vnějšku třídy. Takže to, co je nejlepší způsob, jak to udělat?

Položena 09/10/2008 v 04:34
zdroj uživatelem
V jiných jazycích...                            


17 odpovědí

hlasů
435

Deklarace třída by měla být v záhlaví souboru (nebo ve zdrojovém souboru, pokud není sdílená).
Soubor: foo.h

class foo
{
    private:
        static int i;
};

Ale inicializace by měla být ve zdrojovém souboru.
Soubor: foo.cpp

int foo::i = 0;

V případě, že inicializace je v záhlaví souboru pak každý soubor, který obsahuje soubor záhlaví bude mít definici statický člen. Tedy ve fázi odkazu dostanete linker chyby, jako kód pro inicializaci proměnné budou definovány ve více zdrojových souborů.

Poznámka: Matt Curtis: poukazuje na to, že C ++ umožňuje zjednodušení výše uvedeného, pokud je statický člen proměnná je const typu int (např int, bool, char). Pak můžete deklarovat a inicializovat členské proměnné přímo v deklaraci třídy v záhlaví souboru:

class foo
{
    private:
        static int const i = 42;
};
Odpovězeno 09/10/2008 v 04:36
zdroj uživatelem

hlasů
70

Pro proměnné :

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

To je proto, že tam může být pouze jedna instance foo::iv programu. Je to jakýsi ekvivalent extern int iv záhlaví souboru a int ive zdrojovém souboru.

Aby konstantní můžete dát hodnotu přímo do deklarace třídy:

class foo
{
private:
    static int i;
    const static int a = 42;
};
Odpovězeno 09/10/2008 v 04:41
zdroj uživatelem

hlasů
16
int foo::i = 0; 

Je správné syntaxe pro inicializaci proměnnou, ale musí jít ve zdrojovém souboru (cpp) spíše než v záhlaví.

Protože se jedná o statickou proměnnou překladač musí vytvořit pouze jednu kopii. Musíte mít čáru „int foo: i“ někteří, kde v kódu říci kompilátoru, kde se to dalo jinak dostanete chybu spojení. Pokud tomu tak je v záhlaví dostanete kopii v každém souboru, který obsahuje hlavičku, takže si vynásobte definované chyby symbolů ze spojky.

Odpovězeno 09/10/2008 v 04:42
zdroj uživatelem

hlasů
18

S kompilátorem Microsoft [1], statické proměnné, které nejsou intlike mohou být definovány v souboru záhlaví, ale mimo deklaraci třídy, pomocí specifické Microsoft __declspec(selectany).

class A
{
    static B b;
}

__declspec(selectany) A::b;

Všimněte si, že neříkám, že je to dobré, jen říkám, že může být provedeno.

[1] V dnešní době, více než překladače podporu MSC __declspec(selectany)- alespoň gcc a řinčení. Možná i víc.

Odpovězeno 09/10/2008 v 06:45
zdroj uživatelem

hlasů
10

Nemám dostatek rep sem přidat toto jako komentář, ale IMO je to dobrý styl psát záhlaví s # include stráže stejně, což jak poznamenává Paranaix před pár dny by se zabránilo chybu multiple-definition. Pokud jste již používáte samostatný soubor CPP, že to není nutné, aby jeden používat jen pro inicializaci statické neintegrálního členů.

#ifndef FOO_H
#define FOO_H
#include "bar.h"

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

Nevidím potřebu používat samostatný soubor CPP za to. Jistě, je to možné, ale neexistuje žádný technický důvod, proč byste měli mít.

Odpovězeno 07/01/2012 v 20:42
zdroj uživatelem

hlasů
23

Pro budoucí diváky tuto otázku, chci poukázat na to, že byste se měli vyvarovat, co monkey0506 naznačuje .

hlavičkové soubory jsou prohlášení.

Hlavičkové soubory si sestavují jednou pro každý .cppsoubor, který se přímo nebo nepřímo #includesz nich, a kód vně jakékoliv funkce je provozován při inicializaci programu, předtím main().

Díky tomu, že: foo::i = VALUE;do záhlaví, foo:ibude přiřazena hodnota VALUE(co to je) pro každý .cppsoubor, a tyto úkoly se stane v pořadí neurčitou (určeno linker) předtím, než main()je spuštěn.

Co kdybychom #define VALUEbýt různý počet v jednom z našich .cppsouborů? To bude sestavovat v pohodě a budeme mít žádný způsob, jak zjistit, který z nich vyhraje, dokud spustit program.

Nikdy nedávejte spuštěn kód do záhlaví ze stejného důvodu, že jste nikdy souboru.#include.cpp

zahrnují stráže (kterým souhlasím, měli byste vždy používat) chrání vás z něčeho jiného: stejné záhlaví je nepřímo #included vícekrát při kompilaci jediný .cppsoubor

Odpovězeno 27/12/2012 v 16:37
zdroj uživatelem

hlasů
5

Můžete také přiřazení v souboru záhlaví, pokud použijete záhlaví stráže. Použil jsem tuto techniku ​​pro C ++ knihovny jsem vytvořil. Dalším způsobem, jak dosáhnout stejného výsledku, je použití statické metody. Například...

class Foo
   {
   public:
     int GetMyStatic() const
     {
       return *MyStatic();
     }

   private:
     static int* MyStatic()
     {
       static int mStatic = 0;
       return &mStatic;
     }
   }

Výše uvedený kód má „bonus“ v nevyžadující / zdrojový soubor CPP. Opět platí, že metoda, kterou používat pro své C ++ knihoven.

Odpovězeno 11/04/2013 v 22:50
zdroj uživatelem

hlasů
5

I následovat myšlenku od Karla. Líbí se mi to a teď jsem ji používat stejně. Změnil jsem se trochu notaci a přidat některé funkce

#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));
}

tento výstupy

mystatic value 7
mystatic value 3
is my static 1 0
Odpovězeno 31/12/2013 v 15:45
zdroj uživatelem

hlasů
3

Také pracoval v souboru privateStatic.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
Odpovězeno 26/01/2014 v 12:21
zdroj uživatelem

hlasů
3

A co set_default()metoda?

class foo
{
    public:
        static void set_default(int);
    private:
        static int i;
};

void foo::set_default(int x) {
    i = x;
}

Měli bychom používat pouze set_default(int x)metodu a naše staticproměnná bude inicializovat.

To by nemělo být v nesouladu se zbytkem připomínek, ve skutečnosti to funguje na stejném principu inicializovat proměnné v globálním měřítku, ale pomocí této metody můžeme učinit z něj explicitní (a snadno vidět, rozuměj) místo toho, aby definice variabilní visí tam.

Odpovězeno 23/05/2014 v 15:28
zdroj uživatelem

hlasů
10

Chcete-li inicializovat nějaký druh sloučeniny (fe řetězec), můžete udělat něco takového:

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;
    }
};

Vzhledem k tomu, ListInitializationGuardje statické proměnné uvnitř SomeClass::getList()metody bude konstruována pouze jednou, což znamená, že konstruktér se nazývá jednou. To bude initialize _listproměnnou na hodnotu, kterou potřebujete. Jakékoliv následné volání getListse jednoduše vrátit již inicializovaný _listobjekt.

Samozřejmě máte přístup k _listobjektu vždy voláním getList()metody.

Odpovězeno 23/11/2014 v 11:43
zdroj uživatelem

hlasů
0

Znamená to slouží svůj účel?

//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;
}
Odpovězeno 02/09/2016 v 11:50
zdroj uživatelem

hlasů
0

Jen jsem se chtěl zmínit něco trochu divného, ​​když jsem poprvé narazil tohle.

Potřeboval jsem inicializovat private static datový člen ve třídě šablony.

v .H nebo .hpp, to vypadá asi takhle inicializovat statické datový člen třídy šablony:

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;
Odpovězeno 01/10/2016 v 01:11
zdroj uživatelem

hlasů
5

Vzhledem k tomu, C ++ 17, mohou být statické členy definována v záhlaví s inline klíčové slovo.

http://en.cppreference.com/w/cpp/language/static

„Statický datový člen může být prohlášena za inline Inline statický datový člen mohou být definovány v definici třídy a může určit výchozí člen inicializátor Nepotřebuje out-of-třídy definici:..“

struct X
{
    inline static int n = 1;
};
Odpovězeno 12/07/2017 v 15:34
zdroj uživatelem

hlasů
0

Problém spojovací narazíte je pravděpodobně způsoben:

  • Poskytující jak kvalitní a statické definice člen v souboru záhlaví,
  • Včetně tohoto záhlaví ve dvou nebo více zdrojových souborů.

Jedná se o běžný problém pro ty, kdo začíná s C ++. Statický člen třídy, musí být iniciovány v jediné jednotce překlad tedy v jednom zdrojovém souboru.

Bohužel, statický člen třídy, musí být iniciovány mimo těla třídy. To komplikuje psaní kódu záhlaví-only, a proto jsem s použitím zcela jiný přístup. Můžete poskytnout své statický objekt pomocí statického nebo non-statické funkce třídy, například:

class Foo
{
    // int& getObjectInstance() const {
    static int& getObjectInstance() {
        static int object;
        return object;
    }

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};
Odpovězeno 10/09/2017 v 10:20
zdroj uživatelem

hlasů
2

Statický konstruktor vzor, ​​který pracuje pro více objektů

Jeden idiom bylo navrženo na adrese: https://stackoverflow.com/a/27088552/895245 , ale tady jde o čistší verzi, která nevyžaduje vytvoření nové metody za člena, a čím si příklad:

#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);
}

Viz také: statické konstruktérů v C ++? Musím inicializovat private static objekty

Testováno s g++ -std=c++11 -Wall -Wextra, GCC 7.2, Ubuntu 17.10.

Odpovězeno 19/01/2018 v 08:55
zdroj uživatelem

hlasů
0

One „old-school“ způsob, jak definovat konstanty je nahradit je enum:

class foo
{
    private:
        enum {i = 0}; // default type = int
        enum: int64_t {HUGE = 1000000000000}; // may specify another type
};

Tento způsob nevyžaduje poskytnutí definice, a vyhýbá se dělat neustálé lvalue , které můžete ušetřit nějaké bolesti hlavy, například při omylem ODR-používat ji.

Odpovězeno 13/06/2018 v 21:27
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more