[C++] 클래스의 멤버 함수 호출시 const 여부에 따른 우선 순위 질문

parkon의 이미지

안녕하세요, CERN에서 개발한 ROOT라는 프로그램에서
TObject란 클래스가 있는데요, 이 클래스의 멤버함수 중에
Write()가 const 여부에 따라 아래처럼 두번 정의되어 있습니다.

class TObject {
   // ...
   virtual int Write() const; // 이하 1번 함수라 칭함
   virtual int Write();       // 이하 2번 함수라 칭함
};

그리고 cxx파일에서 1번 Write() const 함수는 열심히 구현되어 있고요,
2번 함수는

int TObject::Write() {
   return ((const TObject*)this)->Write();
}

이렇게 1번 함수를 호출하는 것으로 되어 있습니다.

여기서 제가 마구 헷갈리는 데요, 궁금한 것은

Q1:  obj->Write(); 이렇게 호출하면
1번 함수가 호출되나요 아님 2번 함수가 호출되나요 ?

Q2: 1번 const 를 가진 함수가 잘 돌아가는데
왜 2번 함수를 정의할까요 ?
즉 2번 함수가 필요한 경우가 있나요 ?

Q3: 이 TObject를 상속해서 새로운 클래스를 만들고
저 Write()를 재 정의해서 쓰고 싶은데
저 const 속성은 없어도 되걸랑요.
이 경우, 저 1번과 2번 중 어느 녀석을 재정의 해야 하는 건가요 ?
1번을 재정의해서 대충 돌아가는건 일단 확인했습니다.

nhlsm의 이미지

Q1: obj->Write(); 이렇게 호출하면
1번 함수가 호출되나요 아님 2번 함수가 호출되나요 ?

-> obj 가 const 일 경우, 1번 함수가 호출되고, const 가 아닐 경우, 2번 함수가 호출됩니다.

Q2: 1번 const 를 가진 함수가 잘 돌아가는데
왜 2번 함수를 정의할까요 ?
즉 2번 함수가 필요한 경우가 있나요 ?

-> non-const 객체는 1번 함수를 호출 할 수 없습니다.
때문에, non-const 객체를 위해 필요합니다

Q3: 이 TObject를 상속해서 새로운 클래스를 만들고
저 Write()를 재 정의해서 쓰고 싶은데
저 const 속성은 없어도 되걸랑요.
이 경우, 저 1번과 2번 중 어느 녀석을 재정의 해야 하는 건가요 ?
1번을 재정의해서 대충 돌아가는건 일단 확인했습니다

-> 상황에 따라 다릅니다.
의미적으로 두개 다 구현이 가능하면, 위와 같이 두개 다 구현 하십시오.

p.s:
const 변수는, 해당 변수가 변경되지 않는다. 라는것을 명시적으로 나타내는 것입니다.
때문에, 컴파일러는 const 변수에 값을 쓰는 코드에 대한 에러 처리를 해줍니다.

const 메써드는, 해당 메써드가 클래스의 상태를 변경하지 않는다. 라는것을 명시적으로 나타내는 것입니다.
때문에, 컴파일러는 const 메서드가 클래스의 상태를 변경하는 코드에 대한 에러 처리를 해줍니다.
여기서 클래스의 상태란... 클래스의 멤버 변수들 입니다.
예를 들어, const 메써드에서, 클래스의 멤버변수를 변경하면 컴파일 오류입니다.

const 메써드의 컴파일러 오류처리는 한가지가 더 있습니다. const 메써드에서, non-const 메써드에 대한 호출도 오류 입니다.

컴파일 에러를 의도적으로 유발시킨 뒤 컴파일 에러를 보시면, 짐작을 하실 수 있습니다만...
const 메써드는, 내부적으로 Type *this 를 전달하는게 아니라, const Type *this 를 전달합니다.
때문에, const Type *this 를 변경하는 모든 코드는 컴파일 에러로 간주되는 것입니다.

( 주절 주절 글로 쓰니 복잡한것 같은데, 의미를 잘 따져보면 그리 어렵지 않습니다. )

parkon의 이미지

친절한 답변 감사드립니다.
그런데 const-객체는 non-const 함수를 호출할 수 없지만
반면 const 함수는 모든 객체가 (const-여부에 상관없이) 다 호출할 수 있는거잖아요 ?
그래서 아직 저는 위의 두번째 Q2의 내용이 이해가 안갑니다.

nhlsm의 이미지

그렇네요~ 제가 답변을 반대로 했네요~
"const 객체는 non-const 멤버를 호출 할 수 없습니다." 가 맞습니다.
때문에, 말씀하신대로, 2번 함수의 오버로딩은 불필요해 보입니다.

근데...
반환값이 포인터 또는 레퍼런스 형일 경우에는 const overload 가 필요하지만,
위와 같은 primitive 타입의 반환값을 사용하는 member function 의 const overload 는 좀 이상하네요~

 의 이미지

아직은요.

TObject만 놓고 보면, 2번 함수는 의미가 없습니다. 그게 없어도 비상수 객체에 대해 1번 함수가 알아서 불릴 거거든요. 게다가 2번 함수가 달리 더 하는 일도 없지 않습니까?

TObject는 이름만 봐도 딱 Polymorphism을 지원하기 위한 Base class처럼 보입니다. 자바의 Object 비슷한 거겠죠. 그리고, 1번 및 2번 함수 모두 단순 가상 함수이죠.

제가 개인적으로 무척 존경하는 스콧 마이어스 작가님의 말씀을 들어 봅시다.

Scott Meyers wrote:
1. 순수 가상 함수를 선언하는 목적은 파생 클래스에게 함수의 인터페이스만을 물려주려는 것입니다.
2. 단순 가상 함수를 선언하는 목적은 파생 클래스로 하여금 함수의 인터페이스뿐만 아니라 그 함수의 기본 구현도 물려받게 하자는 것입니다.
(Effective C++ 항목 34: 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자)

즉, TObject는 파생 클래스에게 Write함수의 상수 버전/비상수 버전 인터페이스를 제공합니다. 또한 기본 구현 역시 제공하는데, 비상수 버전이 상수 버전을 호출함으로써 동일한 기능을 하도록 되어 있습니다.

비록 TObject만을 놓고 보면 비상수 버전을 제거해도 아무런 차이가 없게 되지만, 비상수 버전 가상 함수를 만들어두지 않으면 TObject의 파생 클래스가 비상수 버전만을 따로 오버라이드할 방법이 없어지게 됩니다.

뭐 예컨대, TObject를 상속받은 TChild라는 클래스를 만들었는데, 이 클래스의 특성상 비상수 버전 Write이 상수 버전 Write보다 더 효율적으로 동작할 수 있다고 생각해보세요. 프로그래머는 상수 버전과 비상수 버전을 각기 따로 오버라이드해서 조금이라도 더 성능을 끌어내고 싶어 하지 않을까요?

parkon의 이미지

답변 감사드립니다.
그리고 말씀해 주신 내용이 일리가 있어 보입니다.

그러고보니 예전에 TObject의 Print() const 함수를 재정의하면서 저 const 속성때문에
고생했던 기억이 나는군요.

다시 저 TObject의 함수들을 살펴보니 const 여부에 따라 함수를 이중으로 만들어
놓은게 딱 저 Write()만 그렇군요.
이런 면에선, 어쩜 개발 초기에 말씀하신 이유로 이중으로 만들다가
뒤에 한 버젼만을 만들기로 전략 변경하고,
단지 저 Write() 함수는 호환성 문제로 남겨 둔 것 같기도 하구요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.