여러 소스 파일에서 볼 수 있도록 C ++에서 상수를 정의하고 싶습니다. 헤더 파일에서 정의하는 다음과 같은 방법을 상상할 수 있습니다.
#define GLOBAL_CONST_VAR 0xFF
int GLOBAL_CONST_VAR = 0xFF;
- 값을 returing 일부 기능 (예를 들어
int get_GLOBAL_CONST_VAR()
) enum { GLOBAL_CONST_VAR = 0xFF; }
const int GLOBAL_CONST_VAR = 0xFF;
extern const int GLOBAL_CONST_VAR;
그리고 하나의 소스 파일에서const int GLOBAL_CONST_VAR = 0xFF;
옵션 (1)-확실히 사용하고 싶은 옵션이 아닙니다.
옵션 (2)-헤더 파일을 사용하여 각 개체 파일의 변수 인스턴스 정의
옵션 (3)-IMO는 대부분의 경우 과잉 살인입니다.
옵션 (4)-enum에 구체적인 유형이 없기 때문에 많은 경우 좋지 않을 수 있습니다 (C ++ 0X는 유형을 정의 할 가능성을 추가합니다).
따라서 대부분의 경우 (5)와 (6) 중에서 선택해야합니다. 내 질문 :
- (5) 또는 (6) 무엇을 선호합니까?
- 왜 (5)는 괜찮고 (2)는 괜찮습니까?
답변
(5) 당신이 말하고 싶은 것을 정확하게 말합니다. 또한 컴파일러가 대부분의 시간을 최적화 할 수 있습니다. (6) 반면에 컴파일러는 당신이 결국 그것을 변경할지 여부를 알지 못하기 때문에 컴파일러가 그것을 최적화하지 못하게 할 것입니다.
답변
확실히 옵션 5로 가십시오-유형이 안전하고 컴파일러가 최적화 할 수 있도록합니다 (해당 변수의 주소를 사용하지 마십시오 🙂 또한 헤더에있는 경우 전역 범위를 오염시키지 않도록 네임 스페이스에 붙여 넣으십시오.
// header.hpp
namespace constants
{
const int GLOBAL_CONST_VAR = 0xFF;
// ... other related constants
} // namespace constants
// source.cpp - use it
#include <header.hpp>
int value = constants::GLOBAL_CONST_VAR;
답변
(5)는 GLOBAL_CONST_VAR
모든 번역 단위에서 ICE (Integral Constant Expression)로 정의되기 때문에 (6)보다 “낫습니다” . 예를 들어, 모든 번역 단위에서 배열 크기 및 케이스 레이블로 사용할 수 있습니다. (6)의 경우 GLOBAL_CONST_VAR
정의 된 번역 단위에서 정의 지점 이후에만 ICE가됩니다. 다른 번역 단위에서는 ICE로 작동하지 않습니다.
그러나 (5)는 GLOBAL_CONST_VAR
내부 연결을 제공합니다 . 즉,의 “주소 ID”가 GLOBAL_CONST_VAR
각 번역 단위에서 &GLOBAL_CONST_VAR
다를 것입니다. 즉, 각 번역 단위에서 다른 포인터 값을 제공합니다. 대부분의 사용 사례에서 이것은 중요하지 않지만 일관된 전역 “주소 ID”를 갖는 상수 개체가 필요한 경우 (6)을 사용하여 상수의 ICE-ness를 희생해야합니다. 방법.
또한 상수의 ICE-ness가 문제가되지 않고 (정수 유형이 아님) 유형의 크기가 커지면 (스칼라 유형이 아님) 일반적으로 (6)이 (5)보다 더 나은 접근 방식이됩니다.
(2)는 GLOBAL_CONST_VAR
기본적으로 외부 연결이 있기 때문에 OK가 아닙니다 . 헤더 파일에 넣으면 일반적으로에 대한 여러 정의가 발생 GLOBAL_CONST_VAR
하며 이는 오류입니다. const
C ++의 객체는 기본적으로 내부 연결이 있습니다. 이것이 (5)가 작동하는 이유입니다 (위에서 말했듯 GLOBAL_CONST_VAR
이 각 번역 단위에서 별도의 독립적 인 이유 ).
C ++ 17부터 선언 할 수있는 옵션이 있습니다.
inline extern const int GLOBAL_CONST_VAR = 0xFF;
헤더 파일에서. 이렇게하면 모든 번역 단위 (방법 (5)와 마찬가지로)에서 ICE를 제공하는 동시에 전역 주소 ID를 유지합니다 GLOBAL_CONST_VAR
. 모든 번역 단위에서 동일한 주소를 갖게됩니다.
답변
C ++ 11 이상을 사용하는 경우 컴파일 타임 상수를 사용해보십시오.
constexpr int GLOBAL_CONST_VAR{ 0xff };
답변
상수가 되려면 상수로 표시해야합니다. 이것이 제 생각에는 2가 나쁜 이유입니다.
컴파일러는 값의 const 특성을 사용하여 일부 수학 및 실제로 값을 사용하는 다른 연산을 확장 할 수 있습니다.
5에서 6 사이의 선택-흠; 5 기분이 나아졌습니다.
6)에서 값은 선언에서 불필요하게 분리됩니다.
나는 일반적으로 상수 등 만 정의하는 이러한 헤더 중 하나 이상을 가지고 있으며 다른 ‘영리한’것은 없습니다. 어디서나 쉽게 포함될 수있는 멋진 경량 헤더.
답변
두 번째 질문에 답하려면 :
(2) 단일 정의 규칙을 위반하여 불법입니다. GLOBAL_CONST_VAR
포함 된 모든 파일, 즉 두 번 이상 정의합니다 . (5)는 하나의 정의 규칙이 적용되지 않기 때문에 합법적입니다. 각각 GLOBAL_CONST_VAR
은 포함 된 해당 파일에 로컬 인 별도의 정의입니다. 이러한 모든 정의는 물론 동일한 이름과 값을 공유하지만 주소는 다를 수 있습니다.
답변
C ++ 17 inline
변수
이 멋진 C ++ 17 기능을 통해 다음을 수행 할 수 있습니다.
- 각 상수에 대해 단일 메모리 주소 만 편리하게 사용
- 저장
constexpr
: constexpr extern을 선언하는 방법? - 한 헤더에서 한 줄로 수행
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
컴파일 및 실행 :
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
참고 항목 : 인라인 변수는 어떻게 작동합니까?
인라인 변수에 대한 C ++ 표준
C ++ 표준은 주소가 동일하다는 것을 보장합니다. C ++ 17 N4659 표준 초안
10.1.6 “인라인 지정자”:
6 외부 연결이있는 인라인 함수 또는 변수는 모든 변환 단위에서 동일한 주소를 가져야합니다.
cppreference https://en.cppreference.com/w/cpp/language/inline 은static
지정되어 있지 않은 경우, 그것은 외부 링크가 있습니다.
인라인 변수 구현
다음과 같이 구현되는 방법을 관찰 할 수 있습니다.
nm main.o notmain.o
포함하는:
main.o:
U _GLOBAL_OFFSET_TABLE_
U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i
notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
그리고 man nm
말한다 u
:
“u”기호는 고유 한 글로벌 기호입니다. 이것은 ELF 심볼 바인딩의 표준 세트에 대한 GNU 확장입니다. 이러한 심볼의 경우 동적 링커는 전체 프로세스에서이 이름과 유형이 사용중인 심볼이 하나만 있는지 확인합니다.
이를위한 전용 ELF 확장이 있음을 알 수 있습니다.
GCC 7.4.0, Ubuntu 18.04에서 테스트되었습니다.