문제

C ++에서 개인 정적 데이터 멤버를 초기화하는 가장 좋은 방법은 무엇입니까? 헤더 파일에서 이것을 시도했지만 이상한 링커 오류를 제공합니다.

class foo
{
    private:
        static int i;
};

int foo::i = 0;

나는 이것이 수업 밖에서 개인 회원을 초기화 할 수 없기 때문이라고 생각합니다. 그렇다면 이것을하는 가장 좋은 방법은 무엇입니까?

도움이 되었습니까?

해결책

클래스 선언은 헤더 파일 (또는 공유되지 않은 경우 소스 파일)에 있어야합니다.
파일 : foo.h

class foo
{
    private:
        static int i;
};

그러나 초기화는 소스 파일에 있어야합니다.
파일 : foo.cpp

int foo::i = 0;

초기화가 헤더 파일에 있으면 헤더 파일이 포함 된 각 파일에는 정적 멤버의 정의가 있습니다. 따라서 링크 단계에서 변수를 초기화하기위한 코드가 여러 소스 파일에 정의되므로 링커 오류가 발생합니다.

메모: Matt Curtis : 정적 멤버 변수가 const int 유형 인 경우 C ++가 위의 단순화를 허용한다고 지적합니다 (예 : int, bool, char). 그런 다음 헤더 파일의 클래스 선언 내에서 직접 멤버 변수를 선언하고 초기화 할 수 있습니다.

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

다른 팁

a 변하기 쉬운:

foo.h :

class foo
{
private:
    static int i;
};

foo.cpp :

int foo::i = 0;

이것은 하나의 인스턴스 만있을 수 있기 때문입니다. foo::i 귀하의 프로그램에서. 그것은 일종의 것입니다 extern int i 헤더 파일에서 int i 소스 파일에서.

a 끊임없는 클래스 선언에 값을 바로 넣을 수 있습니다.

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

이 질문의 미래 시청자들을 위해, 나는 당신이 무엇을 피해야한다는 것을 지적하고 싶습니다. Monkey0506이 제안합니다.

헤더 파일은 선언을위한 것입니다.

헤더 파일은 매번 한 번 컴파일됩니다 .cpp 직접 또는 간접적으로 파일을 파일하십시오 #includes 그들과 어떤 함수 외부의 코드는 프로그램 초기화에서 실행됩니다. main().

퍼팅 : foo::i = VALUE; 헤더로 foo:i 값이 할당됩니다 VALUE (그게 뭐든간에)마다 .cpp 파일 및 이러한 과제는 이전에 불확실한 순서 (링커에 의해 결정됨)로 발생합니다. main() 실행됩니다.

우리가한다면 #define VALUE 우리 중 하나에서 다른 숫자가됩니다 .cpp 파일? 그것은 잘 컴파일 할 것이며 우리는 프로그램을 운영 할 때까지 어떤 사람이이기는 지 알 수있는 방법이 없습니다.

당신이 결코 같은 이유로 실행 된 코드를 헤더에 넣지 마십시오. #include.cpp 파일.

가드 포함 (항상 사용해야한다는 데 동의) #include단일을 컴파일하는 동안 여러 번 .cpp 파일

C ++ 17이므로 정적 부재는 헤더에 인라인 예어.

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

"정적 데이터 멤버는 인라인으로 선언 될 수 있습니다. 인라인 정적 데이터 멤버는 클래스 정의에서 정의 될 수 있으며 기본 멤버 이니셜 라이저를 지정할 수 있습니다. 클래스 외 정의가 필요하지 않습니다."

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

Microsoft 컴파일러 [1], 정적 변수는 int-Microsoft 특이점을 사용하여 헤더 파일에서도 클래스 선언을 제외하고 정의 할 수 있습니다. __declspec(selectany).

class A
{
    static B b;
}

__declspec(selectany) A::b;

나는 이것이 좋다고 말하는 것이 아니라, 그냥 할 수 있다고 말합니다.

1] 요즘 MSC 지원보다 더 많은 컴파일러 __declspec(selectany) - 적어도 GCC와 Clang. 어쩌면 더 많은 것일 수도 있습니다.

int foo::i = 0; 

변수를 초기화하기위한 올바른 구문이지만 헤더가 아닌 소스 파일 (.cpp)에 들어가야합니다.

정적 변수이므로 컴파일러는 하나의 사본 만 만들어야합니다. 코드의 "int foo : i"라인이 있어야 코드의 위치에 컴파일러에 위치 할 위치를 알리기 위해 링크 오류가 발생합니다. 헤더에있는 경우 헤더가 포함 된 모든 파일에 사본이 표시되므로 링커에서 곱하기 정의 된 심볼 오류를 얻으십시오.

나는 이것을 댓글로 추가하기에 충분한 담당자가 없지만, IMO는 당신의 헤더를 작성하는 것이 좋습니다. #경비원을 포함하십시오 어쨌든, 몇 시간 전에 Paranaix가 언급 한 바와 같이, 다중 정의 오류를 방지 할 것입니다. 이미 별도의 CPP 파일을 사용하지 않는 한 정적 비 통신 멤버를 초기화하기 위해 단지 하나를 사용할 필요는 없습니다.

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

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

이를 위해 별도의 CPP 파일을 사용할 필요가 없습니다. 물론 가능하지만 기술적 인 이유는 없습니다.

일부 화합물 유형 (Fe String)을 초기화하려면 다음과 같은 작업을 수행 할 수 있습니다.

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

로서 ListInitializationGuard 내부 정적 변수입니다 SomeClass::getList() 방법은 한 번만 구성되므로 생성자가 한 번 호출됩니다. 이것은 할 것입니다 initialize _list 필요한 가치에 대한 변수. 후속 호출 getList 이미 초기화 된 단순히 반환됩니다 _list 물체.

물론 액세스해야합니다 _list 항상 호출하여 물체 getList() 방법.

헤더 가드를 사용하는 경우 헤더 파일에 할당을 포함시킬 수도 있습니다. 나는 내가 만든 C ++ 라이브러리 에이 기술을 사용했습니다. 동일한 결과를 달성하는 또 다른 방법은 정적 메소드를 사용하는 것입니다. 예를 들어...

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

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

위의 코드에는 CPP/소스 파일이 필요하지 않은 "보너스"가 있습니다. 다시 C ++ 라이브러리에 사용하는 방법입니다.

나는 Karl의 아이디어를 따릅니다. 나는 그것을 좋아하고 지금도 그것을 사용합니다. 표기법을 조금 바꾸고 기능을 추가했습니다.

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

이 출력

mystatic value 7
mystatic value 3
is my static 1 0

여러 객체에서 작동하는 정적 생성자 패턴

하나의 관용구가 제안되었습니다. https://stackoverflow.com/a/27088552/895245 그러나 회원 당 새 방법을 만들 필요가없는 청정 버전과 실행 가능한 예가 있습니다.

#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 상류.

또한보십시오: C ++의 정적 생성자? 개인 정적 객체를 초기화해야합니다

테스트 g++ -std=c++11 -Wall -Wextra, GCC 7.3, 우분투 18.04.

또한 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

a set_default() 방법?

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

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

우리는 만 사용하면됩니다 set_default(int x) 방법과 우리 static 변수가 초기화됩니다.

이것은 나머지 의견과 의견이 맞지 않을 것이며, 실제로는 글로벌 범위에서 변수를 초기화하는 것과 동일한 원칙을 따릅니다. 그러나이 방법을 사용함으로써 우리는 정의를 갖는 대신 명시 적으로 (그리고 이해하기 쉬운)를 만듭니다. 거기에 매달려있는 가변.

당신이 직면 한 링커 문제는 아마도 다음과 같습니다.

  • 헤더 파일에서 클래스 및 정적 멤버 정의 제공,
  • 두 개 이상의 소스 파일 에이 헤더를 포함합니다.

이것은 C ++로 시작하는 사람들에게 일반적인 문제입니다. 정적 클래스 멤버는 단일 소스 파일의 단일 번역 단위 IE에서 초기화되어야합니다.

불행히도, 정적 클래스 멤버는 클래스 본문 밖에서 초기화되어야합니다. 이것은 헤더 전용 코드를 작성하는 것이 복잡하므로 상당히 다른 접근 방식을 사용하고 있습니다. 정적 또는 비 정적 클래스 함수를 통해 정적 객체를 제공 할 수 있습니다.

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

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

나는 이것을 처음 만났을 때 나에게 조금 이상한 것을 언급하고 싶었다.

템플릿 클래스에서 개인 정적 데이터 멤버를 초기화해야했습니다.

.h 또는 .hpp에서는 템플릿 클래스의 정적 데이터 멤버를 초기화하는 것이 다음과 같은 것 같습니다.

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

상수를 정의하는 "구식"방법 중 하나는 이들을 enum:

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

이런 식으로 정의를 제공 할 필요가 없으며 일정하게 만드는 것을 피합니다. lvalue, 우연히 두통을 절약 할 수 있습니다. ODR 사용 그것.

이것이 당신의 목적에 도움이됩니까?

//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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top