C++에서 const keyword가 너무 남용된다고 생각지 않으세요 ?

parkon의 이미지

프로그래밍은 전공도 아니고 잘하지도 못하는 초보인데요,
어쩌다 보니 기존의 어떤 라이브러리 위에서 돌아가는 라이브러리 만드는 프로젝트를 맞게 되었습니다.
초보다 보니 좌충우돌 헤매는 건 당연한데요,
하 이놈의 const keyword는 징그럽네요.

상수를 정의할 때, 예를 들어
const double pi=3.13;
이런건 좋은데,
그리고 C에서 strchr, strcmp 등에서 인수를 const char*로 하는 정도도 좋은데

클래스 멤버함수 뒤에 붙은 const는 아주 징그럽네요.
예:

class BaseClass {
public:
   // ...
   void Print(const char* options=0) const;
   int  GetSize() const;
   // ...
}

얼핏 보기엔, 그리고 대부분의 경우엔,
멤버 데이타를 보여 주는 저 Print()나 내부 멤버 데이타의 갯수를 보여주는
GetSize()나 const 키워드가 붙는게 맞아 보이긴 한데

이 베이스 클래스를 기반으로 파생클래스를 만들때
때로는 저 const 키워드들이 치명적인 제약을 주는 군요.

예1. Print(options)의 경우,
파생 클래스에서 저 options에 해당하는 프린팅 옵션에 관계되는 내부 변수를 바꿀 수가 없습니다.

예2. Print(options)의 경우,
파생 클래스에서 필요한 멤버 데이타를 하드 디스크에서 load-on-demand하는 경우,
저 Print() 함수를 부를수가 없습니다.

예3. GetSize()에서 멤버 데이타 갯수를 계산하는 루틴이 좀 복잡해서 연산시간이 좀 걸릴때,
저 갯수를 저장하는 멤버 변수를 가지고
멤버 갯수에 변동이 안 생긴 경운 그 변수값을 돌려주고
변동이 생긴 경우만 GetSize()등이 호출되었을때 계산하는 방법을 쓸 수가 없습니다.

뭐 위 상황들이 다 예외적이긴 한데요,
이런 경우 저런 경우 생각하다보니 결국 원 라이브러리에 있는 기본 클래스들을 부모 클래스로 사용하지 못하거나
아니면 원하지 않은 성능 저하나 코드의 복잡성을 감수해야 하는 경우가 많이 생기는 군요.

더 웃기는 건, 저렇게 const 키워드들이 붙은 라이브러리를 쓰는 순간
그 라이브러리를 기반으로 자신이 만드는 코드들도 똑같이 const 키워드를 남발할 수 밖에 없군요.

그렇다고 또 저 const를 안쓰면 그 코드는 const를 쓰는 코드 속에는 들어갈 수도 없구요.

const 키워드가 실수를 방지하는 효과가 있다는 건 아는데요,
저 멤버 함수 선언 뒤에 붙는 const 키워드는
어느 한 곳에 붙는 순간 그것과 관련된 모든 것들이 그것에 맞춰 져야 한다는 점에서
프로그래밍을 어렵게 만들고, 효율을 저하시키고
또 이식성을 심각하게 떨어지게 만든다는 점에서
아예 나오지 못하게 표준을 정해야 했던게 아닌가 싶네요.

어차피 결론은 없겠지만
그냥 푸념도 좀 하고 싶고,
대규모 프로젝트를 하시는 이곳 고수분들은 이 문제(?)를 어떻게 보시는가 궁금하기도 해서
글 올립니다.

semmal의 이미지

const는 캡슐화하는데 아주 훌륭한 수단입니다.

const가 잘못된 것이 아니라 애초 작성자가 const를 쓸 때, 재사용시에 생기는 문제를 생각하지 않고 작성했거나,
최초 작성한 자가 의도한 재사용이 아닌 다른 방법으로, 사용자가 임의로 확장하려고 할 때 생기는 문제겠지요.
결과적으로 const 자체의 문제가 아니라 const 를 쓰는 사람의 문제일뿐입니다.

이미 캡슐화된 클래스가 맘대로 안만져진다고 투털대는 것은 1.5볼트 건전지에서 9볼트가 왜 안나오느냐고 투덜대는 것과 비슷한 겁니다.
앞서 밝힌바와 같이 건전지가 문제가 아니라, 건전지 회사가 실사용자들이 9볼트를 필요로 했다는 것을 몰랐거나,
사용자가 1.5볼트 건전지를 9볼트칸에 끼워서 생기는 문제지요.

지금 가진게 1.5볼트 밖에 없는데 9볼트에 꼭 전기가 필요하다면, 보기싫더라도 1.5볼트 6개를 직렬로 연결하는 수 밖에 없지요.

------------------------------
How many legs does a dog have?

parkon의 이미지

예를 들어 컨테이너 클래스들의 최상위 추상클래스를 만든다고 할 때,
저 GetSize()에 const 키워드를 붙여야 할까요 아님 안붙여야 할까요 ?

물론 이걸 라이브러리로 배포하지 않고 혼자만 쓸거라면,
또는 다른 사람이 만든 라이브러리를 안쓰고 혼자서 다 구현해서 쓴다면,
상황에 따라 취향에 따라 결정하면 그만이지만
라이브러리로 만들어 배포할 생각이라면 꽤나 골치아픈 문제 같은데요...

GetSize()처럼 저런 뻔한 멤버함수에도 const를 안붙이는 건
멤버 함수 선언 뒤에 붙는 const 키워드는 아예 쓰지 말자 하는것과 비슷할 테고,
그렇다고 붙이자니 상황에 따라 위의 제 경우처럼 불편한 경우가 생길테구요.

더구나 한번 붙이게 되면 이 그 라이브러리를 이용하는 파생 클래스들에서도 const를 남발 할 수 밖에 없고,
안 붙이게 되면 거의 대부분 const를 붙이는게 불가능해 지고,,,

저 const 키워드를 정말로 분명하게 확실한 경우만 쓰는 게 좋다고 한다면,
그런 경우에는 const 키워드의 효력 또한 별거 없으니
처음부터 C++ 표준에 멤버 함수 선언뒤에는 const 키워드를 아예 못 붙이게 표준을 만들었다면
잃는게 별거 없이 많은 고민을 일거에 해소할 수 있지 않았을까 해서 올린 글이었습니다.

뭐 C++ 표준 만들때 당연히 수많은 사람들이 엄청 고민해서 잘 만들었을거라는 건 알지만,
이것 때문에 고생을 많이 해서요...

mete0r의 이미지

일단 GetSize()에는 const를 붙이는게 맞을 것 같습니다.

왜냐하면 객체 "사용자"의 입장에서, 저 함수를 호출하더라도 객체의 "외부적" 상태가 변화하지 않을 것으로 기대할 수 있기 때문입니다. 구체적인 맥락은 모르겠지만 Print(options) 역시 마찬가지일 것 같고요.

다만 여기서 말한 "외부적" 상태와, 실제 객체의 "물리적" 상태는 다를 수 있을 겁니다. 예3.에서 말씀하신대로 데이터를 late loading/load-on-demand 형식을 쓴다면 그렇겠지요. 이런 경우를 위해 "mutable"이라는 키워드가 존재합니다:

class Base {
 
  mutable int computed_size;
 
  int ComputeSize() const {
     /* 내부 변수들을 const 참조하여 크기를 반환한다. */
     ...
     return result;
  }
public:
  Base()
    : computed_size(-1) /* 아직 계산되지 않음 */
  {
  }
  int GetSize() const {
    if (computed_size == -1) {
      computed_size = ComputeSize();
    }
    return computed_size;
  }
  ...
}
mete0r의 이미지

"외부적 상태"라기보다는 "개념적 상태"라는 말이 좀 더 이해하기 쉽겠네요.

parkon의 이미지

앗 mutable이라는 저런 키워드도 있었군요,
(앞에서 밝혔듯이 제가 좀 초보입니다... ^^)
덕분에 좋은 테크닉 하나 배웠습니다.
제가 지금 하고 있는 일에 꽤나 유용할 것 같네요, 감사합니다 ^^

mete0r의 이미지

C++이 어려운게, 단순히 문법이 복잡한 것의 문제가 아니라, 저런 것들을 어떤 경우에 어떻게 활용하느냐, 그렇게 함으로써 어떤 이득을 얻을 수 있느냐 하는 것들이 정리된 형태로 잘 알려져 있지 않기 때문인 것 같습니다. 그런 것들이 숙지되지 않은 채로 오남용 되면 코드가 지옥으로 가지요 ㅎㅎ

가령 const는 객체와 객체를 다루는 함수간에 const-ness에 기반한 어떤 약속을 나타내는 거죠. 이 약속이 잘 설계되면 그것에 기반한 강력한 타입 시스템을 구축할 수 있습니다. functional한 개념들이나, 객체 복사를 최대한 회피하기 위한 캐싱에 사용된다거나 할 수 있지요. 그런데 mutable이 잘못 오용되면 이 시스템 자체가 무너져내릴 수가 있습니다 ㅎㅎ

mutable을 const가 주는 제약을 회피하기 위한 꽁수로 쓰이는 것만은 피하시길 ㅎㅎ

노파심에 길게 적어봤는데, 고깝게 들리진 않았으면 좋겠습니다.

Scarecrow의 이미지

멤버함수 뒤에 붙는 const는 this 포인터가 const라는 뜻입니다.
함수를 선언할때 this 인자를 명시하지 않아서
this에 대해서 const를 딱히 붙힐 자리가 없어 거기 쓰는 거라 보여지는데...

그러니까 만약
function(object *this, argument a) 뭐 이렇게 this도 a처럼 하나의 함수 인자로 표기하게 해놨다면
function(const object *this, argument a)라고 했지
function(object *this, argument a) const 라고 하진 않았겠죠.

this도 하나의 인자니까
다른 인자는 const로 정의할 수 있는데 this만 특별히 표준으로 const라고 못하게 할 이유는 없을듯합니다.

const라고 정의해 놓고(다른 사람이 그렇게 정의해 놓은 라이브러리라던지 뭐던지)
구현하는 과정에서 불가피하게 변경할 일이 생긴다면

const_cast를 이용해서 const인 this를 형변환해서 쓰면 될겁니다.

http://msdn.microsoft.com/en-us/library/bz6at95h%28v=vs.80%29.aspx 여기에 있는 예제코드처럼 쓰면 되겠죠.

parkon의 이미지

고깝긴요,
사실 "mutable을 const가 주는 제약을 회피하기 위한 꽁수로 쓰이는 것만은"
이게 제가 조금 전에 하고 있던 생각이었습니다... ㅎㅎ
어쨌거나 말씀하신 요지는 잘 알겠습니다... ^^

parkon의 이미지

오, const_cast라는 것도 있었군요,
덕분에 제 무식을 드러내고 대신 많이 배워 갑니다.
댓글 감사합니다...^^

mete0r의 이미지

const 남발된 코드 때문에 원치않는 성능 저하를 겪는 경우도 있다 하셨는데요, 해당 코드를 못봐서 모르겠지만 const든 뭐든 오용되면, 충분히 그럴 수 있다고 봅니다.

다만 const-ness를 잘 활용하면, 오히려 캐싱을 사용한 성능 이득이나 concurrency의 무결성 보장에 도움이 될 수도 있습니다.

void do_A(const Object & a);
void do_A_concurrently(const Object & a);
void do_B(Object & a);
void do_B_concurrently(Object & a);
 
...
 
Object object;
 
...
 
const int object_size = object.GetSizeWhichTakesLongTime();
 
/* do_A()가 무슨 작업을 하건 간에 object에 대해서는 const한 작업만을 수행할 수 있기 때문에, 우리는 걱정없이 개념적 상태가 그대로 유지될 것임을 알 수 있다. 가령, 위에서 얻은 object_size 값이 do_A()를 수행한 이후에도 유효하며, 계속 쓸 수 있을 것임을 알 수 있다.
 
만약 object.GetSizeWhichTakesLongTime()이 시간이 오래걸리는 작업이라면, 이렇게 object_size를 미리 얻어놓고 활용하는 것은 수행성능에 이득이 될 것이다. */
do_A(object);
for (int i=0;i<object_size;++i) {
  object.DoSomethingOnItem(i);
}
 
/* 별도 쓰레드에서 A를 수행하기 시작한다. 그러나 우리는 여전히 객체의 상태가 변하지 않을 것임을 알 수 있으며, 미리 얻어놓은 object_size를 계속 사용할 수 있다: */
do_A_concurrently(object);
for (int i=0;i<object_size;++i) { ... }
 
/* do_B()를 수행한 후에는 object의 상태가 어떻게 변할지 알 수 없다. 따라서 우리가 위에서 미리 얻어 놓은 object_size는 이후부터 사용할 수 없다. */
do_B(object);
for (int i=0;i<object.GetSizeWhichTakesLongTime();++i) { ... }
 
/* 별도 쓰레드로 do_B()를 수행하기 시작한다. 이후부터는 객체의 상태가 언제 어떻게 바뀔지 알 수 없다.*/
do_B_concurrently(object);
 
// 다음 루프는 위험하다:
for (int i=0;i<object.GetSizeWhichTakesLongTime();++i) { ... }

그런데 사실 저는 이렇게 const를 잘 써서 무결성을 보장받으려 머리 싸매는 것보다는, 그냥 testcase를 잘 만들어 돌리는 편이 노력 대비 효과가 더 좋은 것 같더라고요.

kukyakya의 이미지

작성하신 예는 적합하지 않은 것 같습니다.

별도의 쓰레드에서 같은 객체에 접근을 할 때에 해당 객체가 변화할 가능성이 있다면 const로 해결할 수 있는 게 아니라 mutex 등의 락을 걸어주어야합니다.

mete0r의 이미지

네. 위에서 쓴 do_B_concurrently() 이후에는 mutex를 사용해야하지요. 그 이전에는 그렇지 않다는 얘길 한겁니다.

parkon의 이미지

말씀하신 것에 대한 댓글이라기 보다는 제 혼자의 푸념입니다... ^^

솔직히 제가 초보다 보니
"캐싱을 사용한 성능 이득이나 concurrency의 무결성 보장" 이 말 뜻을 잘 모르겠어요,
반박이 아니라 말씀하신 말 뜻을 이해를 못하고 있는... -_-

근데 요 근래에 코딩을 좀 해 보다 보니 참 코딩이 힘들더군요.
코드가 물 흐르듯이 자연스러워야 되는데
어떤 난관에 부딪히면 고민 고민 하게 되고
그러다가 바로 이거야 하면서 무릎을 탁 치면서 찾아낸 나름 혜안은
나중에 보니 악수중 초 악수인 경우가 다반사고.

제가 위에서 주절 주절 한 내용도
그놈의 const 키워드만 없었으면 고민할 것도 걱정할 것도 없었을 터인데
(그래서 코드가 좀 더 자연스러워 질 수 있었을 터인데)

그냥 static 멤버 변수 두면 될 일을
그 const-ness 속성 피한다고
전역 변수로 떡칠을 했다가
너무 보기 싫어 결국엔 실상 필요 없는 싱글톤 클래스도 만들고
const 키워드 붙은 놈과 안붙은 놈 이렇게 멤버 함수를 이중으로 만들고... 으... ^^

말씀해 주신 mutable이나 const_cast만 알았더라도,
진작에 여기에 제 고민을 올려 볼 껄 하는 하는 때늦은 후회(?)가 물밀듯이 밀려 오네요... ^^

하지만, 그럼에도 불구하고, 여전히 전 C++의 (const *this에 대한) 지금 규약이 잘못 되었다고 생각합니다.
끝내 감당할 수 없는 자유를 줌으로서 모든 이들을 구속한다는 기분이랄까요... 흠...

저 const 키워드를 멋지게 속일(?) 수 있는 방법이 있으면
저 const 키워드가 코더들에게 주는 그 심리적인 안정(?)은 굉장히 위험하지 않을까요 ?
mete0r님도 이 부분을 경계하라고 저에게 조언을 주신 거구요.

그렇다면 이 모든 게, 처음부터 const *this에 대한 const 규칙은 없으니
코더들이 코딩할 때 잘 알아서 해라... 라고 하는게 옳지 않았을까 싶어요.
하지만 역시 제가 아는 게 없다 보니 제가 하는 말에 확신을 가지기는 힘들군요^^

yhsuk의 이미지

캐싱을 사용한 성능 이득에 대한 얘기는
mete0r님 글에 이미 나와있지만, uint64_t GetSize() const 같은 멤버가 있을 때 GetSize를 매번 호출하는 게 아니라
uint64 size = GetSize();를 해서 size를 사용해서 성능이득을 꽤할 수 있다는 뜻입니다.
const 키워드가 멤버함수에 있으면 호출 후에도 내부상태는 변하지 않을 것이라 생각할 수 있으니까요.

const 키워드 자체의 효용성에 얘기하신 것에 말씀을 요약해 보면
const 키워드 제약을를 속일 방법이 있으니 쓸데없이 귀찮게 하지 말고 const 제약을 없애는게 좋지 않겠냐는 말씀이신데 보는 관점이 다른 것 같습니다.
음... 보는 관점이 다르다고 말씀드렸지만, 실제로는 이 내용이 고려되서 문법이 만들어졌을 것이라고 생각합니다.

아예 문법적으로 막는 것이 오히려 쉽습니다. 지원하지 않으면 되니까요.
하지만, 일단 const로 막고 나서 안 될 경우에 mutable 키워드나 const_cast의 사용을 신중하게(!) 고려하게 할 수 있습니다. 말 그대로 "신중하게" 입니다.
아예 제약자체가 없으면, 멤버를 변경할 수 있는 메쏘드 호출이 자신도 모르게 가능하게 되니까요.
말씀하신 mutable keyword는 임시변수 값을 저장할 멤버가 필요한 경우에 보통 사용하며,
이 임시변수가 다른값으로 변했다고 해서 인스턴스가 담고 있는 데이터가 변경되었다고는 보지 않아야 합니다.
const_cast를 사용하는 경우에도 꼭 필요한 경우에만 사용해야지 남발해서는 안됩니다.

제약을 가함으로써 실수를 방지하는 매커니즘은 const 키워드 외에도 여러 곳에서 발견됩니다.
1. C에서는 묵시적 변환이 이루어질 수 있어 컴파일 되던것이 C++로 컴파일 할 때는 안되는 경우가 있습니다.
컴파일 되게 하려면 강제 형변환을 하면 됩니다. "그럼 귀찮게 강제 형변환을 왜 하느냐 묵시적 변환을 지원하게 만들면 되지" 라는 얘기와 같습니다.
이런것들은 형식으로써 실수를 방지하게 만드는 역할을 하고 const와도 비슷한 역할을 한다고 볼 수 있겠습니다.

2. C에서 에러코드로 실패를 감지하던 것을 C++에서는 예외를 던지도록 한 것.
함수가 에러코드를 리턴한다면 함수의 실패를 항상 에러코드로 확인할 수 있습니다. 코더들이 코딩할 때 잘 알아서 하면 되지요.
하지만 C++에서는 모든 코더가 항상 함수호출후 에러코드를 확인한다고 확신할 수 없기 때문에 예외가 도입되었습니다.

기타로 파라미터에 const 키워드를 사용한다면 input 파라미터라고 알려주는 역할도 수행합니다.

Signature :) - "여유를 갖고 행동하되 게을러지지 말자"

sblade의 이미지

immutability 즉 한번 만들어진 객체는 "고칠" 수 없음을 보장하면 편해지는 경우가 있습니다.

첫째로 불필요한 deep copy를 피할 수 있는 경우가 있습니다. 리스트 X = [a,b,c,d,e] 가 존재할때 X는 그대로 둔 채 만약 각 원소의 field u 가 10이면 20 으로 바꾸고 싶다고 합시다. 즉 만약 c.u == 10 이면 c.u = 20 으로 바꾸고 싶은 경우입니다.

이때 원본 X를 그대로 두고 싶기 때문에, 일반적으로는 X를 Y로 "deep copy" 한 다음 각 원소를 체크해서 field를 바꾸겠죠. 그런데 deep copy는 불필요한 시간과 노력이 많이 듭니다. 만약 오직 c만 바뀌면 된다고 하면, a,b,d,e를 복사할 필요 자체가 없죠. 같은 것이니까요. 문제는 복사할 시점에서는 얘네가 고쳐질지 안고쳐질지 모르기 때문에 일단 복사를 해야 된다는 겁니다.

이때 만약 각각의 객체를 고칠 수 없다는 것이 보장된다면? 각각의 원소를 검사한 다음 고칠 필요가 없는 것은 reference만 넘기고, 고칠 필요가 있는 것만 "다시 만들어서" 집어넣으면 됩니다. 이런 류의 테크닉을 implicit sharing 이라고 합니다. 즉 고칠 필요가 있는 것만 다시 만들어서 넣는거죠.

caching도 비슷한 맥락입니다. 데이터가 변하지 않는다는 것을 알기 때문에 한번 계산해 놓은 값은 끝까지 유효하죠. 리스트 X가 immutable 하면, X의 길이는 항상 5입니다.

그리고 concurrency 도 비슷한 이야기입니다. threading 의 가장 큰 어려움은, 두 개 이상의 task가 같은 데이터에 접근할 때 같은 내용을 보고 있는지를 보장할 수 있느냐는 거죠. 그걸 보장하려고 mutex 와 condition들을 이리저리 걸어서 한번에 한 task만 접근할 수 있도록 "동기화" 하는 건데, 만약에 데이터가 변할 수 없다면 (변하려면 새로 만들어야 한다면), 이런 고민 자체를 할 필요가 없습니다. 어차피 내용은 변하지 않으니까요.

한편 const 키워드에 대해 지적하신 것 자체는 매우 맞는 이야기라고 봅니다. const 가 "전염성" 이 있는 것도 문제라면 문제고, 대부분의 잘 짜여진 라이브러리들은 const 버전 함수와 const가 없는 버전 함수를 동시에 제공하고 있습니다. 용도에 맞게 알아서 쓰라는 거죠. C++ 의 전반적인 분위기가 그렇습니다. "알아서 해라" 죠. 그런데 C++의 분위기가 너무 "알아서 해라" 이다 보니까 "애초에 초보도 실수할 수 없게, 넣을 수 있는데는 모두 const를 넣자" 라는 도그마가 생겨났죠. 그게 const-correctness 인 셈입니다.

개인적으로 생각하는 더 큰 문제는 한 문장에 const 를 5번도 쓸 수 있다는 겁니다. 3번은 흔하죠

const return_type function_name(const argument_type) const

처럼 "input도 안변할거고, output 도 변하면 안되고, 해당 instance 도 안고친다" 는 용도로 const를 3번이나 쓰는 경우는 너무 많고, 여기에다 포인터까지 const를 걸면 5번도 쓸 수 있습니다. 눈에 익지 않으면 정말 헷갈리죠.

C++이 programmability 보단 performance가 훨씬 중요하던 시기의 상당히 옛날 언어이고 (70년대 말 디자인), 만든 사람이 상당히 고집센 연구원 => 대학 교수 인 사람이라는 것을 생각하면 이러한 점이 크게 이해가 안가는 것은 아닙니다.

philnet의 이미지

어떤 일이든 어느 정도의 시행착오가 필요한 법 아니겠습니까? 프로그래밍에서도 마찬가지고요. 그런 시행착오를 최소한으로 줄이는데 도움 받을 수 있는 경험있는 사람이 곁에 있으면 참 고마운 일이겠습니다만, 그러긴 쉽지 않죠. ^^;

프로그래밍 하기에도 빠듯하실 테지만, 좋은 책을 읽기를 권합니다. 당장 C++로 개발하신다면 스캇 메이어 아저씨의 Effective C++ 시리즈가 큰 도움이 되실 겁니다. 지금 고민하시는 const 와 mutable, 그리고 const_cast<>와 관련된 내용들에 대해서도 잘 설명되어 있습니다.

그리고 리팩토링, 디자인 패턴 같은 책들도 언젠가 시간을 내서 보시면 훌륭한 프로그래머가 되는데 도움이 될 것이고요, 또 좋은 책을 보다 보면 자연스레 다른 좋은 책들을 알게 되실 겁니다.

초보(?) 개발자의 행운을 빕니다.

parkon의 이미지

여기에 글 올린 덕분에 많이 배웁니다.
또 제가 하는 일에 도움도 많이 되었구요.
이젠 const가 붙은 멤버 함수들 더 이상 두렵지 않다는... ^^
제 글에 관심 가져 주신 모든 분들, 복 받으실거예요 ~~~

zelon의 이미지

고수는 아니지만, 답글 추가해봅니다. :)

저는 다른 이유로 Java 에 왜 멤버 함수(메서드)에 const 가 안될까 찾아봤는데,

parkon 님과 비슷한 생각을 그분께서 하셨던지, Java 에는 함수에 const(final)이 없더라구요 -0-;

-----------------------------------------------------------------------
GPL 오픈소스 윈도우용 이미지 뷰어 ZViewer - http://zviewer.wimy.com
블로그 : http://blog.wimy.com

parkon의 이미지

zelon님, 댓글 감사합니다.
덕분에 위에 제가 주절거린 내용이 완전히 엉터리는 아닐지도 모른다는
위안을 얻게 되었습니다.
자바에서는 저 함수 선언 끝에 const를 붙일수 없다는 것도 배웠구요... ^^