C ++에서 개인 정적 멤버를 초기화하는 방법은 무엇입니까?
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;
초기화가 헤더 파일에있는 경우 헤더 파일을 포함하는 각 파일에는 정적 멤버의 정의가 있습니다. 따라서 링크 단계 중에 변수를 초기화하는 코드가 여러 소스 파일에 정의되므로 링커 오류가 발생합니다.
주 : 매트 커티스 : 아웃 포인트 C는 ++의 단순화를 허용하는, 상기 정적 부재 변수 CONST INT 형인 경우 (예를 들어 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
파일 을 컴파일하는 동안 동일한 헤더가 간접적으로 여러 번 d
C ++ 17부터 정적 멤버는 인라인 키워드로 헤더에 정의 될 수 있습니다 .
http://en.cppreference.com/w/cpp/language/static
"정적 데이터 멤버는 인라인으로 선언 될 수 있습니다. 인라인 정적 데이터 멤버는 클래스 정의에서 정의 될 수 있으며 기본 멤버 이니셜 라이저를 지정할 수 있습니다. 클래스 외부 정의는 필요하지 않습니다."
struct X
{
inline static int n = 1;
};
Microsoft 컴파일러 [1]를 사용하면 int
유사 하지 않은 정적 변수를 Microsoft specific을 사용하여 헤더 파일에서 정의 할 수 있지만 클래스 선언 외부에서 정의 할 수도 있습니다 __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"줄이 있어야합니다. 그렇지 않으면 링크 오류가 발생합니다. 그것이 헤더에 있다면 헤더를 포함하는 모든 파일에 사본을 얻을 수 있으므로 링커에서 곱셈 된 심볼 오류를 얻습니다.
복합 유형 (fe 문자열)을 초기화하려면 다음과 같이 할 수 있습니다.
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()
.
여기에 주석으로 추가 할 충분한 담당자가 없지만 IMO는 어쨌든 #include 가드로 헤더를 작성 하는 것이 좋습니다. 몇 시간 전에 Paranaix가 언급 한 것처럼 다중 정의 오류를 방지합니다. 이미 별도의 CPP 파일을 사용하지 않는 한 정적 비 통합 멤버를 초기화하기 위해 파일을 사용할 필요는 없습니다.
#ifndef FOO_H
#define FOO_H
#include "bar.h"
class foo
{
private:
static bar i;
};
bar foo::i = VALUE;
#endif
이를 위해 별도의 CPP 파일을 사용할 필요가 없습니다. 물론 가능하지만 기술적 인 이유는 없습니다.
헤더 가드를 사용하는 경우 헤더 파일에 할당을 포함시킬 수도 있습니다. 이 기술을 내가 만든 C ++ 라이브러리에 사용했습니다. 동일한 결과를 얻는 또 다른 방법은 정적 메서드를 사용하는 것입니다. 예를 들어 ...
class Foo
{
public:
int GetMyStatic() const
{
return *MyStatic();
}
private:
static int* MyStatic()
{
static int mStatic = 0;
return &mStatic;
}
}
위 코드에는 CPP / 소스 파일이 필요없는 "보너스"가 있습니다. 다시 C ++ 라이브러리에 사용하는 방법입니다.
여러 객체에 작동하는 정적 생성자 패턴
하나의 관용구가 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);
}
참조 : C ++ 정적 생성자는? 개인 정적 객체를 초기화해야합니다
g++ -std=c++11 -Wall -Wextra
GCC 7.3, Ubuntu 18.04로 테스트되었습니다 .
나는 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
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
약 어떤 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 ++로 시작하는 사람들에게 일반적인 문제입니다. 정적 클래스 멤버는 단일 변환 단위, 즉 단일 소스 파일에서 초기화되어야합니다.
불행히도 정적 클래스 멤버는 클래스 본문 외부에서 초기화해야합니다. 이것은 헤더 전용 코드 작성을 복잡하게하므로 상당히 다른 접근법을 사용하고 있습니다. 정적 또는 비 정적 클래스 함수를 통해 정적 객체를 제공 할 수 있습니다. 예를 들면 다음과 같습니다.
class Foo
{
// int& getObjectInstance() const {
static int& getObjectInstance() {
static int object;
return object;
}
void func() {
int &object = getValueInstance();
object += 5;
}
};
상수를 정의하는 "구식"방법 중 하나는 상수를 다음과 같이 바꾸는 것입니다 enum
.
class foo
{
private:
enum {i = 0}; // default type = int
enum: int64_t {HUGE = 1000000000000}; // may specify another type
};
이 방법을 사용하면 정의를 제공 할 필요가없고 상수 lvalue를 만들지 않아도되므로 실수로 ODR을 사용 하는 등의 두통을 줄일 수 있습니다.
나는 이것을 처음 만났을 때 조금 이상한 것을 언급하고 싶었다.
템플릿 클래스에서 개인 정적 데이터 멤버를 초기화해야했습니다.
.h 또는 .hpp에서 템플릿 클래스의 정적 데이터 멤버를 초기화하는 방법은 다음과 같습니다.
template<typename T>
Type ClassName<T>::dataMemberName = initialValue;
이것이 당신의 목적에 도움이됩니까?
//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;
}
참고 URL : https://stackoverflow.com/questions/185844/how-to-initialize-private-static-members-in-c
'Programming' 카테고리의 다른 글
localStorage 값의 최대 크기는 얼마입니까? (0) | 2020.02.13 |
---|---|
R을 다시 시작하지 않고 패키지를 언로드하는 방법 (0) | 2020.02.13 |
포인터 "역 참조"는 무엇을 의미합니까? (0) | 2020.02.13 |
매직 넘버는 무엇이며 왜 나쁜가요? (0) | 2020.02.13 |
검색 메소드가 'null'을 리턴하거나 리턴 값을 생성 할 수없는 경우 예외를 발생시켜야합니까? (0) | 2020.02.13 |