Pergunta

Qual é a melhor maneira de inicializar, um membro de dados estáticos privado em C ++? Eu tentei isso no meu arquivo de cabeçalho, mas isso me dá erros de vinculador estranho:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

Eu estou supondo que isto é porque eu não posso inicializar um membro privado de fora da classe. Então, qual é a melhor maneira de fazer isso?

Foi útil?

Solução

A declaração de classe deve estar no arquivo de cabeçalho (ou no arquivo de origem se não for compartilhado).
Arquivo: foo.h

class foo
{
    private:
        static int i;
};

Mas a inicialização deve estar no arquivo de origem.
Arquivo: foo.cpp

int foo::i = 0;

Se a inicialização é no cabeçalho do arquivo, em seguida, cada arquivo que inclui o arquivo de cabeçalho terá uma definição do membro estático. Assim, durante a fase de link que você receberá erros de vinculador como o código para inicializar a variável será definido em vários arquivos de origem.

Nota: Matt Curtis: pontos que C ++ permite a simplificação do acima, se a variável de membro estático é do tipo int const (por exemplo int, bool, char). Você pode então declarar e inicializar a variável membro diretamente dentro da declaração de classe no arquivo de cabeçalho:

class foo
{
    private:
        static int const i = 42;
};

Outras dicas

Para um variável :

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

Isso ocorre porque só pode haver uma instância de foo::i em seu programa. É uma espécie de equivalente a extern int i em um arquivo de cabeçalho e int i em um arquivo de origem.

Para um constante você pode colocar o valor diretamente na declaração da classe:

class foo
{
private:
    static int i;
    const static int a = 42;
};

Para os futuros espectadores desta questão, quero salientar que você deve evitar o monkey0506 está sugerindo .

Os arquivos de cabeçalho são para declarações.

Os arquivos de cabeçalho se compilado uma vez para cada arquivo .cpp que direta ou indiretamente #includes eles, e fora do código de qualquer função é executado na inicialização do programa, antes main().

Ao colocá: foo::i = VALUE; no cabeçalho, foo:i será atribuído o VALUE valor (qualquer que seja) para cada arquivo .cpp, e essas atribuições vai acontecer em uma ordem indeterminada (determinado pelo vinculador) antes main() é executado

E se nós #define VALUE ser um número diferente em um de nossos arquivos .cpp? Ele irá compilar multa e teremos nenhuma maneira de saber qual ganha até que execute o programa.

Nunca coloque código executado em um cabeçalho para a mesma razão que você nunca #include um arquivo .cpp.

incluem guardas (que eu concordo que você deve sempre usar) protegê-lo de algo diferente: o mesmo cabeçalho sendo indiretamente #included várias vezes ao compilar um único arquivo .cpp

Desde C ++ 17, membros estáticos podem ser definidos no cabeçalho com o em linha palavra-chave.

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

"Um membro de dados estáticos podem ser declarados em linha Um membro de dados static inline pode ser definido na definição da classe e pode especificar um inicializador membro padrão Ela não precisa de uma definição de out-of-classe:.".

struct X
{
    inline static int n = 1;
};

Com um compilador Microsoft [1], variáveis ??estáticas que não são int-like também pode ser definida em um arquivo de cabeçalho, mas fora da declaração de classe, usando o específico __declspec(selectany) Microsoft.

class A
{
    static B b;
}

__declspec(selectany) A::b;

Note que não estou dizendo que isso é bom, eu apenas dizer que pode ser feito.

[1] Estes dias, mais do que compiladores MSC __declspec(selectany) apoio - pelo menos gcc e clang. Talvez ainda mais.

int foo::i = 0; 

é a sintaxe correta para inicializar a variável, mas deve ir no arquivo de origem (.cpp) em vez de no cabeçalho.

Porque é uma variável estática as necessidades do compilador para criar apenas uma cópia do mesmo. Você tem que ter uma linha "foo int: i" em algum lugar no seu código para dizer ao compilador onde colocá-lo de outra forma você recebe um erro de link. Se isso está em um cabeçalho você receberá uma cópia em cada arquivo que inclui o cabeçalho, erros de símbolos assim se multiplicam definida a partir do vinculador.

Eu não tenho rep suficiente aqui para adicioná-lo como um comentário, mas IMO é bom estilo de escrever seus cabeçalhos com # incluem guardas de qualquer maneira, que, como observado por Paranaix há poucas horas impediria um erro de múltipla definição. A menos que você já está usando um arquivo CPP separado, não é necessário usar um só para inicializar os membros não-integrais estáticos.

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

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

Eu não vejo nenhuma necessidade de usar um arquivo CPP separado para isso. Claro, você pode, mas não há nenhuma razão técnica pela qual você deve ter a.

Se você quiser inicializar algum tipo composto (string F. E.), você pode fazer algo assim:

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

Como o ListInitializationGuard é uma variável estática dentro do método SomeClass::getList() será construída uma única vez, o que significa que o construtor é chamado uma vez. Isto irá initialize _list variável para o valor que você precisa. Qualquer chamada subseqüente a getList simplesmente retornará objeto _list já inicializado.

Claro que você tem para acesso a objetos _list sempre chamando o método getList().

Você também pode incluir a atribuição no cabeçalho do arquivo se você usar guardas de cabeçalho. Eu tenho usado essa técnica para uma biblioteca C ++ que eu criei. Outra maneira de conseguir o mesmo resultado é usar métodos estáticos. Por exemplo ...

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

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

O código acima tem o "bônus" de não necessitar de um arquivo CPP / fonte. Mais uma vez, um método que eu uso para minhas bibliotecas C ++.

Eu sigo a idéia de Karl. Eu gosto dele e agora eu usá-lo também. Eu mudei um pouco a notação e adicionar algumas funcionalidades

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

este saídas

mystatic value 7
mystatic value 3
is my static 1 0

estática padrão construtor que trabalha para vários objetos

Um idioma foi proposto em: https://stackoverflow.com/a/27088552/895245 mas aqui vai um versão mais limpo que não requer a criação de um novo método por membro, e um exemplo executável:

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

GitHub montante .

Veja também: em C ++? Eu preciso para inicializar objetos estáticos privados

Testado com g++ -std=c++11 -Wall -Wextra, GCC 7.3, o Ubuntu 18.04.

Além disso, trabalhar no arquivo 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

Que tal um método set_default()?

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

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

Nós só teria que usar o método set_default(int x) e nossa variável static iria ser inicializado.

Isto não estaria em desacordo com o resto dos comentários, na verdade, segue o mesmo princípio de inicializar a variável em um escopo global, mas usando esse método, torná-la explícita (e fácil de ver de entender) em vez de tendo a definição da variável pendurado.

O problema vinculador que você encontrou é provavelmente causado por:

  • Fornecer tanto classe e definição de membro estático no arquivo de cabeçalho,
  • Incluindo este cabeçalho em dois ou mais arquivos de origem.

Este é um problema comum para aqueles que começa com C ++. membro da classe estática deve ser inicializado em uma única unidade de tradução ou seja, no arquivo de origem único.

Infelizmente, o membro da classe estática deve estar fora inicializado do corpo da classe. Isso complica escrever código só-header, e, portanto, estou usando abordagem completamente diferente. Você pode fornecer seu objeto estático através estático ou função de classe non-static por exemplo:

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

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};

Eu só queria mencionar algo um pouco estranho para mim quando eu encontrei pela primeira vez isso.

eu precisava para inicializar um membro de dados estáticos privada em uma classe de modelo.

no .h ou .hpp, que é algo como isto para inicializar um membro de dados estáticos de uma classe de modelo:

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;

Uma maneira "old-school" para definir constantes é para substituí-los por um enum:

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

Desta forma, não necessita de fornecer uma definição, e evita fazer a constante lvalue , que pode poupar algumas dores de cabeça , por exemplo quando você acidentalmente ODR-use -lo.

Será que isso serve o seu propósito?

//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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top