2011-01-26 7 views
12

정적 변수 초기화에 관한 많은 질문을 읽은 후에도이 문제가 const 변수에 어떻게 적용되는지 확실하지 않습니다. 나는 static 키워드를 읽은 내용에 따르면 헤더 파일과 정적 초기화 실패의 const 변수

static const std::string path1 = "/xyz/abc"; 
static const std::string path2 = "/etc"; 

이 필요하지 않습니다, 심지어 여기에 사용되지 않는 :

나는 빌드 스크립트에 의해 생성 된 헤더 파일 config.h에 다음 코드의 종류가 있습니다.

내 질문 : 위의 코드가 정적 초기화 실패로 이어질 수 있습니까?

나는이 (가) 헤더 파일 myclass.h에 다음과 같은 경우 :

class MyClass 
{ 
public: 
    MyClass(const std::string& str) : m_str(str) {} 
    std::string Get() const { return m_str; } 

private: 
    std::string m_str; 
} 

const MyClass myclass1("test"); 

이 정적 초기화에 문제를 제기 할 것인가?

오른쪽으로 이해한다면 const 변수가 내부 연결을 가지고 있기 때문에 두 경우 모두 문제가 없어야합니까?

편집 :이 사용에 관한

#include <config.h> 
#include <myclass.h> 

std::string anotherString(path1 + myclass1.Get()); 

int main() 
{ 
    ... 
} 

또 다른 질문 :

main.cpp에서 :

(때문에 dribeas 대답) 아마 나는 같은 사용 사례에 관심이 있음을 언급해야한다 case :이 경우 컴파일러가 path2을 최적화 할 예정입니까?

답변

9

C++ 03 표준 문서에서 필요한 정보를 바로 얻으려고했습니다. 네임 스페이스 수준에서 정의 3.5.3 객체를 섹션에 따르면

const는 기본적으로 내부 연결를 선언하십시오 const static 선언에 관한

: 여기 내가 찾은 것입니다. 또한 static은 네임 스페이스 수준의 개체가 내부 연결을 갖도록 선언하므로 개체를 선언 할 필요가 없습니다. static const.

변수는 이들이 항상들을 사용하는 다른 정적 오브젝트 전에 정의되는 헤더 파일에서 정의되기 때문에 부속서 D.2 정적 초기 실패 대하여

The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.5).

에 따른 또

. 섹션 3.6.2.1에서

:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

않음 1 :정적 오브젝트에 constructor에 변수 괜찮을 전달 수단.

않음 2 : 그러나 변수가 정적 오브젝트의 비 인라인 생성자에서 참조하는 경우 문제가 발생할 수있다 :

둘 절 3.6.2.1도 3.6.2.3에서 어느 순서로 지정되지 main의 첫 번째 문 앞에 동적 초기화가 수행되면 다른 컴파일 단위의 정적 객체가 으로 초기화됩니다.

는 다음을 고려하십시오 const 변수의 복사본을 가지고

// consts.h 
#include <string> 

const std::string string1 = "ham"; 
const std::string string2 = "cheese"; 

// myclass.h 
#include <string> 

class MyClass 
{ 
public: 
    MyClass(); 
    MyClass(std::string str); 
    std::string Get() { return memberString; } 
private: 
    std::string memberString; 
} 

// myclass.cpp 
#include "consts.h" 
#include "myclass.h" 

MyClass::MyClass() : memberString(string1) {} 

MyClass::MyClass(std::string str) : memberString(str) {} 

// main.cpp 
#include <iostream> 
#include "consts.h" 
#include "myclass.h" 

MyClass myObject1; 
MyClass myObject2(string2); 

using namespace std; 

int main() 
{ 
    cout << myObject1.Get(); // might not print "ham" 
    cout << myObject2.Get(); // will always print "cheese" 
} 

myclass.cpp 때문에 가 호출 될 때,이 초기화되지 않을 수 있습니다.그래서 그래, 헤더 파일에 정의 된 const 변수는 정적 초기화 실패하는 경향이 방식으로 사용할 수 있습니다

지금까지 내가이 정적 초기화를 필요로하지 않는 변수에 적용 되는가 볼 수 있습니다 : C++ 03 표준, 섹션 3.6.2.1에서

:

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

12

첫 번째 정의에서는 config.h을 포함하는 각 컴파일 단위에 path1을 배치합니다. 이를 피하려면 헤더 파일에 변수를 정의하지 마십시오. 예를 들어,

extern const std::string path1; 
extern const MyClass myclass1; 

및 구현 파일에 정의 : 일반적으로 당신은 extern로 헤더에 변수를 선언 할 것 config.cpp :

const std::string path1 = "/xyz/abc"; 
const MyClass myclass1("test"); 

때때로 하나의 구현 파일에서만 사용할 수있는 상수 변수가 필요합니다. 그런 다음 파일 범위에서 해당 변수를 static으로 선언 할 수 있습니다.

static const std::string path1 = "/xyz/abc"; 

static은 더 이상 사용되지 않습니다. staticextern이 암시되는 경우가 있지만 어디서 어떻게 잊어 버리기 때문에 일반적으로 모든 네임 스페이스 수준 변수에 명시 적으로 지정합니다.

+1

이 static'는 구현 파일에서 사용할 수있는 '사실이 아니다. –

+0

물론 C++의 구현 파일에 대한 개념이 전혀 없습니다. 단어를 변경하려고합니다. – Philipp

+2

찾고있는 문구는 구현 파일 대신 "번역 단위"라고 생각합니다. 값은 헤더에 선언되고 최대 하나의 번역 단위로 정의되어야합니다. 이 경우 const이고 내부 연결을 암시하므로 링크 타임에 다중 정의 기호 오류가 발생하지 않지만이 머리글을 포함하는 모든 변환 단위에 동일한 기호가 정의되어 있습니다. 내부 링크가 있기 때문에 링크가 끊어지지는 않지만 컴파일러는 중복을 제거하지 못할 수도 있습니다. 한 번 헤더에서 문자열의 정의를 제거하여 실행 파일에서 8MB를 면도했습니다. – legalize

8

정적 초기화 실패라고하는 것은 하나의 네임 스페이스 수준 변수가 이전에 초기화되지 않았거나 초기화되지 않은 다른 네임 스페이스 수준 변수에 할당 된 값에 의존 할 때 문제가됩니다. 두 가지 예에서 그러한 의존성은 없으며 아무런 문제가 없어야합니다.

이를, 다른 한편으로는, 오류의 유형과 경향 : foo 모두가 일정한 경우에도, foobar 전에 초기화된다는 보장은 없습니다

// header.h 
extern const std::string foo; 

// constant.cpp 
const std::string foo("foo"); 

// main.cpp 
#include "header.h" 
const std::string foobar(foo+"bar"); 
int main() { 
    std::cout << foobar << std::endl; 
} 

. 이는 프로그램 동작이 정의되지 않았으며 "foobar", "bar"또는 die를 잘 인쇄 할 수 있음을 의미합니다.

+0

원래 질문에서 유스 케이스를 업데이트했습니다. 이것은 정확하게 내가 알고 싶은 시나리오입니다. –

+0

나는 그것에 대해 감히 논평하지 않을 것이다. 내 첫 번째 생각은 괜찮을 것입니다 (상수는 번역 단위에 국한되며 두 번째 상수 앞에 정의됩니다). 가능한 경우 해당 구조를 피합니다. 필자가 가장 잘 알고있는 가장 간단한 방법은 전역 변수 대신 내부적으로 정적 변수를 가진 함수를 사용하는 것입니다 :'inline myclass & global_object() {myclass instance; 인스턴스 반환; }'std :: string other (global_object(). Get());가 반드시 작동하게 될 것입니다 ... –

+2

어쨌든, 나는 그것들을 완전히 피하고 그들의 초기화로 네임 스페이스 레벨 변수를 제거하려고 시도 할 것이다. 네임 스페이스 수준 변수의 초기화는 간단하지 않습니다. 두 단계에서 발생합니다. 두 단계에서 상수로부터 인스턴스화 된 모든 전역 변수 *가 먼저 값을 가져오고 두 번째 단계에서는 비 상수에 종속 된 모든 항목이 다음과 같이 초기화됩니다. 둘 이상의 번역 단위가 프로그램에 링크되어있는 경우 정의되지 않은 순서로 정의 순서 (동일한 번역 단위 내에서). –

2

정적 초기화 실패는 에 의존하는 정적 변수를 참조합니다. 일부 static const 변수를 정의하는 것만으로는 문제의 원인이되지 않습니다.