C ++ 클래스에서 정적 변수를 초기화 하시겠습니까?


81

클래스의 일부 함수가 실제로 객체에 액세스하지 않는다는 것을 알았으므로 static. 그런 다음 컴파일러는 액세스하는 모든 변수가 정적이어야한다고 말했습니다. 지금까지는 꽤 이해할 수 있습니다. 다음과 같은 문자열 변수가 많이 있습니다.

string RE_ANY = "([^\\n]*)";
string RE_ANY_RELUCTANT = "([^\\n]*?)";

수업에서 등등. 나는 static const그들이 결코 변하지 않기 때문에 그것들을 모두 만들었습니다 . 그러나 내 프로그램은 클래스 밖으로 이동하는 경우에만 컴파일됩니다. 그렇지 않으면 MSVC ++ 2010은 "정적 상수 정수 변수 만 클래스 내에서 초기화 될 수 있습니다."라고 불평합니다.

그것은 불행한 일입니다. 해결 방법이 있습니까? 나는 그들이 속한 수업에 남겨두고 싶습니다.

답변:


131

클래스 내부에서 초기화 할 수 없지만 소스 파일에서 클래스 외부에서 초기화 할 수 있습니다.

// inside the class
class Thing {
    static string RE_ANY;
    static string RE_ANY_RELUCTANT;
};

// in the source file
string Thing::RE_ANY = "([^\\n]*)";
string Thing::RE_ANY_RELUCTANT = "([^\\n]*?)";

최신 정보

나는 당신의 질문의 첫 번째 줄을 눈치 챘 - 당신이 하지 않는 이러한 기능을 만들고 싶어 static, 당신이 그들을 만들고 싶어 const. 그들을 만드는 것은 static그들이 더 이상 객체와 관련된 없음을 의미 (그래서 그들은 액세스 할 수없는 비 정적 멤버), 그리고이 유형의 모든 개체와 공유 할 데이터의 정적 수단을. 이것은 당신이 원하는 것이 아닐 수도 있습니다. 그들을 만드는 것은 const구성원을 수정할 수는 없지만 여전히 액세스 할 수 있음을 의미합니다.


2
객체의 어떤 것도 액세스하지 않고 참조 인수로 제공되는 임시 변수 만 액세스합니다. 따라서 둘 다 conststatic.
Felix Dombek

8
@Felix : const불가능합니다 this. 수정하지 않는다는 의미 this이며 static메서드에 대한 것이 없습니다 .
Matthieu M.

2
@ androidplusios.design : 나는 따르지 않는다. 당신은 필요 string가 형식 지정자를 필요로하는 변수 정의, 그리고 유형이기 때문입니다 string. 원하는 ()경우 선언자를 둘러 쌀 수 있지만 의미는 변경되지 않습니다.
Mike Seymour

4
@ androidplusios.design : 그렇습니다. 그러나 정의 구문을 사용하여 정의하고 초기화해야하며, 여기에는 이전에 선언되었는지 여부에 관계없이 유형 지정자가 포함됩니다. 그것이 언어가 지정되는 방법입니다.
Mike Seymour

4
@ androidplusios.design : 또는 모든 불일치, 중복성 및 모호함과 함께 구문이있는 그대로인 이유를 묻는 경우 : 훨씬 단순한 언어에서 수십 년에 걸쳐 진화했기 때문입니다.
Mike Seymour

33

Mike Seymour가 정답을 주었지만 추가하려면 ...
C ++를 사용하면 컴파일러에서 알 수 있듯이 정적 const 정수 유형 만 클래스 본문에서 선언하고 정의 할 수 있습니다 . 따라서 실제로 다음을 수행 할 수 있습니다.

class Foo
{
    static const int someInt = 1;
    static const short someShort = 2;
    // etc.
};

다른 유형으로는이를 수행 할 수 없습니다.이 경우 .cpp 파일에서 정의해야합니다.


17

C ++ 11부터는 constexpr.

class stat {
    public:
        // init inside class
        static constexpr double inlineStaticVar = 22;
};

이제 다음을 사용하여 변수에 액세스 할 수 있습니다.

stat::inlineStaticVar

1
int, double, pointers 등에 확실히 좋습니다. 그러나 이것은 문자열이 리터럴이나 참조가 아니기 때문에 질문의 문자열과 함께 작동하지 않습니다.
Roi Danton 2017

1
좋은 지적. 하지만 constexpr char RE_ANY[] = "([^\\n]*)";또는 constexpr std::string_view RE_ANY("([^\\n]*)", 9);.
0ax1

16

정적 멤버 변수는 클래스에서 선언 한 다음 외부에서 정의해야합니다!

해결 방법은 없으며 실제 정의를 소스 파일에 넣으십시오.


설명에 따르면 정적 변수를 올바른 방식으로 사용하지 않는 것처럼 보입니다. 변경되지 않는 경우 대신 상수 변수를 사용해야하지만 설명이 너무 일반적이어서 더 많은 것을 말할 수 없습니다.

정적 멤버 변수는 항상 클래스의 모든 인스턴스에 대해 동일한 값을 유지합니다. 한 개체의 정적 변수를 변경하면 다른 모든 개체에 대해서도 변경됩니다 (실제로 클래스의 인스턴스 없이도 액세스 할 수 있습니다. 즉 : 객체).


3
그것들은 이제 const입니다-정적 멤버 함수에서 사용할 수 있도록 정적이어야합니다. 이 규칙이 클래스 내부에서 선언되고 클래스 외부에서 정의되어야하는 이유는 무엇입니까? 그것은 나에게별로 말이되지 않는다.
Felix Dombek

2
@Felix Dombek : 그 이유는 컴파일하고 링크하는 각 소스 파일에 대해 클래스가 선언 (/ 가능)되기 때문이라고 생각하지만 실제 변수는 한 번만 정의해야합니다. 이는 extern다른 소스 파일에 정의 된 변수 로 명시 적으로 선언해야하는 동일한 이유 입니다.
peoro

1
@peoro : 합리적으로 보인다! 그렇다면 왜 정수 데이터 유형에 허용됩니까?
그것도

1
@Felix Dombek : 정수 유형에도 허용되지 않습니다. 이 struct : 를 정의 하고 소스 파일에서 정의하지 않고 struct A { static int x; };액세스하려고하면 코드가 링크되지 않습니다. A::xint A::x;
peoro

2
@Felix Dombek : 표준 불만이 아닙니다. ISO C ++는 상수가 아닌 정적 멤버의 클래스 내 초기화를 금지합니다. 정수 const 정적 멤버에 대해서만 그렇게 할 수 있으며, 이는 정적 const 정수 변수가 실제로 메모리에 저장되지 않고 컴파일 타임에 상수로 사용되기 때문입니다.
peoro

9

정적 변수가 상수 변수와 동일하지 않다는 점을 추가 할 가치가 있다고 생각합니다.

클래스에서 상수 변수 사용

struct Foo{
    const int a;
    Foo(int b) : a(b){}
}

그리고 우리는 그렇게 선언 할 것입니다

fooA = new Foo(5);
fooB = new Foo(10);
// fooA.a = 5;
// fooB.a = 10;

정적 변수의 경우

struct Bar{
    static int a;
    Foo(int b){
        a = b;
    }
}
Bar::a = 0; // set value for a

그렇게 사용되는

barA = new Bar(5);
barB = new Bar(10);
// barA.a = 10;
// barB.a = 10;
// Bar::a = 10;

여기서 무슨 일이 일어나는지 봅니다. Foo가 인스턴스화되므로 Foo의 각 인스턴스와 함께 인스턴스화되는 상수 변수는 Foo의 각 인스턴스에 대해 별도의 값을 가지며 Foo에 의해 전혀 변경 될 수 없습니다.

Bar와 마찬가지로 Bar :: a에 대한 값은 Bar의 인스턴스 수에 관계없이 하나뿐입니다. 그들은 모두이 값을 공유하며 Bar의 모든 인스턴스로 액세스 할 수도 있습니다. 정적 변수는 또한 public / private에 대한 규칙을 준수하므로 Bar의 인스턴스 만 Bar :: a의 값을 읽을 수 있도록 만들 수 있습니다.


1
허 ... 아무도 'new'의 끔찍한 쓸데없는 사용을 지적하지 않았다는 사실이 재밌습니다. 이 질문에 대해서는별로 걱정할 필요는 없지만, 필요하지 않을 때는 'new'를 사용하지 마십시오.
thecoshman

의 끔찍하고 불필요한 사용을 제거하십시오 new. ;-)
Roi Danton

1
나는 이제 그것이 얼마나 오래된 코드가 나쁜지에 대한 역사적인 예라고 생각합니다. 이 시점에서 나는 그것을 제거하는 것이 해로울 것이라고 생각합니다.
thecoshman

8

다른 답변 위에 추가하십시오. 복잡한 정적 멤버 를 초기화하려면 다음과 같이 할 수 있습니다.

평소와 같이 정적 멤버를 선언하십시오.

// myClass.h
class myClass
{
static complexClass s_complex;
//...
};

그렇게하는 것이 사소하지 않은 경우 클래스를 초기화하는 작은 함수를 만드십시오. 이것은 정적 멤버가 초기화 될 때만 호출됩니다. (complexClass의 복사 생성자가 사용되므로 잘 정의되어야합니다.)

//class.cpp    
#include myClass.h
complexClass initFunction()
{
    complexClass c;
    c.add(...);
    c.compute(...);
    c.sort(...);
    // Etc.
    return c;
}

complexClass myClass::s_complex = initFunction();

5

일부 답변은 약간 오해의 소지 가있는 것 같습니다. .

그럴 필요가 없습니다 ...

  • 할당은 선택 사항 이므로 초기화 할 때 일부 정적 개체에 을 할당합니다 .
  • .cpp동일한 헤더 파일 에서 수행 할 수 있으므로 초기화를위한 다른 파일만듭니다 .

또한 inline키워드를 사용하여 일반 변수처럼 동일한 클래스 범위에서 정적 개체를 초기화 할 수도 있습니다 .


동일한 파일에 값없이 초기화

#include <string>
class A
{
    static std::string str;
    static int x;
};
std::string A::str;
int A::x;

동일한 파일의 값으로 초기화

#include <string>
class A
{
    static std::string str;
    static int x;
};
std::string A::str = "SO!";
int A::x = 900;

inline키워드를 사용하여 동일한 클래스 범위에서 초기화

#include <string>
class A
{
    static inline std::string str = "SO!";
    static inline int x = 900;
};

2
그들 모두의 최고의 대답. 추가 inline하고 모든 연결 오류가 사라졌습니다.
Contango

'인라인 변수는 C ++ 17 확장입니다'. 그것이 제가 받고있는 메시지입니다
Ssenyonjo

@Ssenyonjo C ++ 언어 표준을 C ++ 17 이상으로 설정해야합니다. 즉, Visual Studio에서 프로젝트-> 속성-> C / C ++-> 언어-> C ++ 언어 표준-> ISO C ++ 17 또는 ISO C ++ 최신으로 이동합니다. 그리고 GCC에서 플래그 사용 -std=c++17또는 -std=c++20
Beyondo

2

목표가 헤더 파일에서 정적 변수를 초기화하는 것이라면 ( "header only"관용구를 고수하는 경우 원하는 * .cpp 파일 대신), 다음을 사용하여 초기화 문제를 해결할 수 있습니다. 주형. 템플릿 화 된 정적 변수는 여러 기호를 정의하지 않고도 헤더에서 초기화 할 수 있습니다.

예를 보려면 여기를 참조하십시오.

클래스 템플릿의 정적 멤버 초기화


1

선택적으로 모든 상수를 .h 파일에 선언하지 않고 .cpp 파일로 이동합니다. 익명 네임 스페이스를 사용하여 cpp 모듈 너머로 보이지 않게합니다.

// MyClass.cpp

#include "MyClass.h"

// anonymous namespace
namespace
{
    string RE_ANY = "([^\\n]*)";
    string RE_ANY_RELUCTANT = "([^\\n]*?)";
}

// member function (static or not)
bool MyClass::foo()
{
    // logic that uses constants
    return RE_ANY_RELUCTANT.size() > 0;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.