[완료]C++ 문법 질문드립니다. (상수의 선언과 정의에 대해서)
글쓴이: wsong / 작성시간: 목, 2011/03/24 - 12:24오후
안녕하세요. 요즘 C++공부하고 있다가 도무지 이해가 안되는게 있어서요.혹시 아시는 분 있으시면 답변 부탁드립니다.
// 원본 클래스 class vector { public: double x; double y; const static double pi; /* const static double pi(void) { return 3.1415927; } */ vector (double a=0, double b=0) { x = a; y = b; } double surface() { return x * y; } }; //const double vector::pi = 3.1415927;・・・① vector::pi = 3.1415927;・・・②
vector 클래스 내부의 상수 pi가 있는데, 보통 정수타입의 상수를 제외하고는 외부에서 정의해야한다고 알고 있습니다.그래서 클래스 외부에서 상수를 정의하고 있는데요. ①번 코드를 사용하면 아무 문제 없이 컴파일 되고요. ②번 코드를 사용하면 아래 에러메세지가 나옵니다.
...test01.cpp(31) : warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
에러메세지를 보면 상수를 정의하는 경우는 자동적으로 int로 형변환하는 거 같은데, 왜 컴파일시에 이런 형변환 처리를 하는지 이해가 잘 안됩니다.
질문을 정리하면,
질문1)왜 정수타입의 상수를 제외하고는 외부에서 정의해야하는 제한이 있는지.
질문2)상수를 정의하는 경우는 자동적으로 int로 형변환하는 거 같은데, 왜 컴파일시에 이런 형변환 처리를 하는지.
질문3)아래 코드는 정의임에도 불구하고, 왜 형을 상수앞에 쓴건지.
//const double vector::pi = 3.1415927;・・・①
Forums:
C++ 상수는 형을 지정하게 되어 있습니다.
C의 #define 상수와는 다르죠. 그렇게해서 형 안정성을 추구하는 방식입니다.
2번은 형을 지정하지 않았기 때문에 int라고 간주한 겁니다.
답변 감사드립니다. 이제야 조금 이해가 되었네요.
답변 감사드립니다. 이제야 조금 이해가 되었네요.
음..
질문1)왜 정수타입의 상수를 제외하고는 외부에서 정의해야하는 제한이 있는지.
>> 클래스 내부에서 할지 외부에서 할지의 문제에서 뭘하는가 하면 바로 '초기화' 입니다.
그렇다면 초기화를 클래스 밖에서 해야만 하는 것은 무엇인가 하면 바로 static 맴버 변수 입니다.
이런 문법을 보통 C++ 책들에서는 'static 맴버 초기화' 라고 부릅니다.(윤성우의 열혈강의 C++ 프로그래밍)
왜 클래스 외부에서 초기화 해야 하는가 하면 static 맴버 변수와 static 맴버 함수에 대해 공부해보세요...
답은 static 맴버는 실상은 전역 맴버이기 때문입니다. 단지 접근 권한만 해당 클래스의 내부 함수들에게 허락되는 것입니다.
질문2)상수를 정의하는 경우는 자동적으로 int로 형변환하는 거 같은데, 왜 컴파일시에 이런 형변환 처리를 하는지.
>> 이런 에러 메세지 원인까지 분석할 필요는...
질문3)아래 코드는 정의임에도 불구하고, 왜 형을 상수앞에 쓴건지.
>> ??
static 과 const 는 C++ 에서 아주 중요하게 사용됩니다.
더 자세하게 설명하기는 힘들고요.. C++ 책을 참고하시면 금방 아실 수 있으실겁니다.
이해하기 쉽게 설명해주셔서 감사합니다. 덕분에 알 거
이해하기 쉽게 설명해주셔서 감사합니다. 덕분에 알 거 같습니다. ^^
어떤 컴파일러를 사용하시는지는 모르겠지만 ②는 최소한
어떤 컴파일러를 사용하시는지는 모르겠지만
②는 최소한 g++에서는 허용하지 않는 문법입니다.
위의 코드에서 int foo; 의 명령행은 변수를 "정의" 함과 동시에 메모리에 "할당"하는 결과를 낳습니다.
마지막 명령문을 제외하면, 위의 코드는 class Bar의 "정의" 일 뿐입니다. 따라서, 그것의 멤버변수인 int foo 역시 "정의"만 합니다...int 타입의 foo놈이 있을수도 있다.
하지만, 아무런 메모리 할당이 일어나지 않습니다.
그리고, Bar abc; => 이 명령문에 의해, 앞서 "정의"한 클래스 타입의 변수가 "선언" 되어 메모리에 "할당" 되는 결과를 초래합니다.
여기서 static_foo는 모든 class Static_Bar 객체가 공유하는, 오로지 하나의 멤버변수입니다.
모든 Static_Bar 객체가 동일한 메모리 주소를 가리키게 될것이므로, 예제2의 class Bar 처럼
각 객체가 인스턴스화 될때마다, 객체마다 개별적인 static_foo를 할당해주는건 단순히 공간의 낭비일 뿐입니다.
그래서,
예제1의 전역변수 foo처럼
static_foo 라는 전역변수 하나를 선언해버리고, 모든 Static_Bar의 객체들은 그 전역변수를 가리키게 될것입니다.
하지만, 여기서 문법적 모순이 발생합니다.
앞서 설명했듯이, class ABC{ ... }; 은 단순히 "정의"일 뿐입니다.
그 "정의" 자체는 어떠한 메모리 "할당"을 야기시키는 선언을 하면 안됩니다.
단지, Foo라고 불리는 놈이 있는데, 걔는 멤버로 foo라는 int 타입의 변수를 가지고 있더라....식의 카더라 일뿐이지
그 "정의"자체로 어떠한 객체,변수의 "선언"/"할당"이 일어나면 안되기 때문입니다. (최소한 **문법적**으로는)
실질적인 객체의 선언/할당은 그 객체를 인스턴스화 시키는 명령문의 시점에서 발생해야합니다.( 다시말하지만, **문법적**으로는 )
따라서
예제3의
static int foo; //멤버변수로써
명령문은
예제1의
int foo; //전역변수로써
명령문 처럼
변수를 "선언"/"할당"하는 문법적 의미를 갖지 못합니다.
그렇다고 앞서 설명했듯이, non static 멤버변수처럼 객체가 할당할때마다 새로운 공간을 선언하는것은 공간 낭비입니다.
또한, static 멤버변수는 객체에 종속된것이 아니라서 어떠한 객체를 선언하지 않아도( 즉, 해당 타입의 객체가 never 인스턴스화 되지 않더라도 )
접근할수 있어야하는데, 객체 할당시에 static 멤버변수도 할당해버리면 이런 접근방법과 모슨이 발생합니다.
예제4는 컴파일 당시에는 아무런 문법적 오류가 없이 나옵니다. 왜냐면 vector class는 double 타입의 pi가 존재한다는것이 이미 밝혀졌거든요
하지만, 링커가 불평을 합니다.
"거짓말! 있다고 하더니, 실제론 없잖아!"
똑같은 접근으로서, extern 변수를 생각해볼수 있습니다.
여기서 extern 키워드는 변수 foo가 사실 어딘가 다른곳에서 선언/할당 되어있는 변수이다.
그러니깐, 이거 선언/할당 하지마라.
라는 의미 입니다.
foo 란놈이 존재하고, 타입은 int다라는것을 알려줬기에 컴파일러가 불평하지 않습니다.
그래서, extern int foo로 해놓고 컴파일하면 잘 됩니다만
다른 소스 어딘가에서 int foo로 할당을 안하면 또다시 링커가 불평합니다.
"또 거짓말! 있다고 하더니, 실제론 없잖아!"
즉, class밖에서 해야하는
static 멤버 변수의 초기화문은 (가령 ①같이 )
값을 assign, =, 하는게 목적이 아닙니다.
그것은 변수를 선언/할당하는것입니다.
따라서,
②는 문법적으로 잘못된 선언입니다.
타입을 명시해줘야하거든요.
제 추측으로 보아
글쓰신 분께서 사용하신 컴파일러는 타입을 명시하지 않고
선언한 변수 명령문 ②을 보고
"아마, int 타입을 의미하지않았을까??" 라고 지맘대로 생각해버리곤
암시적으로 int vector::pi = 3.14로 해석해버린것입니다. 아주 주제넘은놈이죠.
그러니, 다음과 같은 경고 메세지가 나오겠죠.
..test01.cpp(31) : warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
다시말하지만, g++에서는 그러한 경우, 맘대로 해석해서 알아서 타입을 정하는것이 아닌
에러 메세지를 뿌립니다. 그리고, 제 생각에는, 이것이 올바른 결정으로 보여지네요.
컴파일러를 바꾸는것을 고려해보세요.
댓글 달기