개인 멤버 함수를 헤더에 넣는 이유는 무엇입니까?


16

비공개 멤버 변수를 C ++ 헤더에 넣는 이유에 대한 대답은 클래스의 크기를 인스턴스가 선언되는 지점에서 알아야 컴파일러가 스택에서 적절하게 움직이는 코드를 생성 할 수 있다는 것입니다.

왜 비공개 회원을 헤더에 넣어야합니까?

그러나 클래스 정의에서 개인 함수를 선언 해야하는 이유가 있습니까?

대안은 본질적으로 pimpl 관용구이지만 불필요한 간접 지시는 없을 것입니다.

이 언어 기능은 역사적 오류 이상입니까?

답변:


11

개인 멤버 함수는 virtual이며, vtable을 사용하는 C ++의 일반적인 구현에서는 클래스의 모든 클라이언트가 특정 순서와 가상 함수 수를 알아야합니다. 하나 이상의 가상 멤버 함수가 인 경우에도 적용됩니다 private.

컴파일러 구현 선택은 언어 사양에 영향을 미치지 않기 때문에 이것이 "마차 앞에 카트를 넣는"것처럼 보일 수 있습니다. 그러나 실제로 C ++ 언어 자체는 vtables를 사용 하는 실제 구현 ( Cfront ) 과 동시에 개발되었습니다 .


4
"구현 선택은 언어에 영향을 미치지 않아야한다"는 C ++ 만트라와 거의 정반대입니다. 이 사양은 특정 구현을 요구하지 않지만, 특히 효율적인 구현 선택의 요구 사항을 충족시키기 위해 특별히 만들어진 많은 규칙이 있습니다.
Ben Voigt

이것은 매우 그럴듯하게 들립니다. 이것에 소스가 있습니까?
Praxeolitic

@Praxeolitic : 가상 메소드 테이블 에 대해 읽을 수 있습니다 .
Greg Hewgill

1
@Praxeolitic- 'The Annotated C ++ References Manual'( stroustrup.com/arm.html ) 의 오래된 사본을 찾으십시오 -저자는 다양한 구현 세부 사항과 언어의 모양에 대해 이야기합니다.
Michael Kohne

이것이 실제로 질문에 대한 대답인지 확실하지 않습니다. 나에게 그것은 단지 하나의 특정 패싯을 해결하지만 나머지는 남겨 둡니다. 가상아닌 개인 멤버 함수를 헤더 에 넣어야 합니까? 물론, 일관성 / 간단성에 대한 한 가지 논거가 있습니다. 그러나 이상적으로는 우리는 기계적이고 철학적 인 이론적 근거도 있습니다. 그래서 나는 이것이 매우 유익한 효과가있는 매우 신중한 디자인 선택이라고 설명하는 대답을 추가했습니다.
underscore_d

13

정의 외부의 클래스에 메소드를 추가 할 수 있도록 허용 한 경우 모든 파일의 어느 위치에서나 메소드를 추가 할 수 있습니다 .

그러면 모든 클라이언트 코드에 개인 및 보호 된 데이터 멤버에 대한 간단한 액세스 권한이 즉시 부여됩니다.

클래스 정의를 마치면, 파일을 확장하기 위해 일부 파일을 저자가 특별히 축복 한 것으로 표시 할 수 없습니다. 평평한 번역 단위 만 있습니다. 따라서 컴파일러에게 특정 메소드 세트가 공식적이거나 클래스 작성자가 축복 한 것이라고 알리는 유일한 합리적인 방법은 클래스 내부에서 메소드를 선언하는 것입니다.


C ++에서 메모리에 직접 액세스 할 수 있습니다. 이는 일반적으로 클래스와 동일한 메모리 레이아웃으로 섀도우 유형을 만들고, 내 자신의 메서드를 추가하거나 (또는 ​​모든 데이터를 공개로 만드는) 사소한 일임을 의미합니다 reinterpret_cast. 또는 개인 기능 코드를 찾거나 분해 할 수 있습니다. 또는 심볼 테이블에서 함수 주소를 조회하거나 직접 호출하십시오.

이러한 액세스 지정자는 이러한 공격을 막으려 고 시도하지 않습니다. 이것이 불가능하기 때문입니다. 클래스 사용 방법 만 나타냅니다.


1
클래스 정의는 클라이언트 코드에서 숨겨진 함수 컨테이너 엔티티를 참조 할 수 있습니다. 대안으로, 이것은 반전 될 수 있으며 클라이언트 코드에서 숨겨진 완전한 전통적인 클래스 정의는 공개 멤버만을 설명하고 그 크기를 공개 할 수있는 가시적 인 인터페이스를 지정할 수 있습니다.
Praxeolitic

1
물론, 컴파일 모델은 클라이언트 코드가 자체 버전의 숨겨진 컨테이너 또는 추가 액세서가있는 다른 가시적 인터페이스를 간섭하는 것을 막을 수있는 합리적인 방법을 제공하지 않습니다.
쓸모없는

좋은 지적이지만 어쨌든 헤더에없는 모든 멤버 함수의 정의에 해당되지 않습니까?
Praxeolitic

1
모든 멤버 함수는 클래스 내부에서 선언 됩니다. 요점은 클래스 정의에서 적어도 선언되지 않은 새 멤버 함수를 추가 할 수 없다는 것입니다 (하나의 정의 규칙은 여러 정의를 방지 함).
쓸모없는

그렇다면 개인 함수 규칙의 컨테이너 하나는 어떻습니까?
Praxeolitic

8

수락 된 답변은 가상 개인 기능에 대해 설명 하지만 OP의 요청보다 상당히 제한된 질문의 특정 측면에만 응답합니다. 따라서 우리는 왜 비가 상 개인 함수를 헤더 에 선언해야 합니까?

또 다른 대답은 클래스가 하나의 블록으로 선언되어야한다는 사실을 불러 일으킨 후에는 봉인되어 추가 할 수 없다는 것입니다. 헤더에 개인 메소드를 선언하지 않고 다른 곳에서 정의하려고 시도하여 수행하는 작업입니다. 좋은 지적이야 클래스의 일부 사용자가 다른 사용자가 볼 수없는 방식으로 클래스를 확장 할 수있는 이유는 무엇입니까? 전용 메소드는 그 일부이며 여기에서 제외되지 않습니다. 그러나 그것들이 포함되어 있는지 물어 보면 약간 긴장된 것처럼 보입니다. 왜 클래스 사용자가 그들에 대해 알아야합니까? 그들이 보이지 않는다면, 사용자는 아무것도 추가 할 수 없었으며, 그 결과는 예쁘다.

따라서 기본적으로 개인 메서드를 포함하는 것이 아니라 사용자가 볼 수 있도록 특정 지점을 제공한다는 답변을 제공하고 싶었습니다. 공개 선언이 필요한 비가 상 개인 기능의 기계적인 이유는 Herb Sutter의 GotW # 100에서 Pimpl 관용구 에 대한 이론적 근거의 일부입니다. 나는 우리 모두 그것에 대해 알고 있기 때문에 Pimpl에 대해서는 계속하지 않을 것입니다. 그러나 관련 비트는 다음과 같습니다.

C ++에서 헤더 파일 클래스 정의의 내용이 변경되면 해당 클래스의 모든 사용자는 클래스의 사용자가 액세스 할 수없는 개인 클래스 멤버에 대한 변경 만 있었더라도 다시 컴파일해야합니다. C ++의 빌드 모델은 텍스트 포함을 기반으로하고 C ++에서는 호출자가 개인 구성원의 영향을받을 수있는 클래스에 대해 두 가지 주요 사항을 알고 있다고 가정하기 때문입니다.

  • 크기 및 레이아웃 : [멤버 및 가상 기능-자체 설명이 필요하고 성능은 우수하지만 여기에있는 이유는 아님]
  • 함수 : 호출 코드는 비 개인 함수로 오버로드되는 액세스 할 수없는 개인 함수를 포함 하여 클래스의 멤버 함수에 대한 호출을 분석 할 수 있어야 합니다. 개인 함수가 더 일치하면 호출 코드가 컴파일되지 않습니다. (C ++은 안전상의 이유로 접근성 검사 전에 과부하 해결을 수행하기 위해 의도적으로 설계 결정을 내 렸습니다. 예를 들어, 함수의 접근성을 개인에서 공공으로 변경한다고해서 법적 호출 코드의 의미가 바뀌지 않아야한다는 느낌이 들었습니다.)

물론 Sutter는위원회 회원으로서 매우 신뢰할 수있는 출처이므로 "고의적 인 설계 결정"을 알고 있습니다. 그리고 나중에 변경된 의미 체계를 피하거나 나중에 실수로 액세스 가능성을 잃지 않도록 개인 메서드를 공개적으로 선언해야한다는 생각이 가장 설득력있는 이유 일 것입니다. 고맙게도, 지금까지 모든 것이 무의미 해 보였습니다!


" 하지만 당신은 왜 그런지 물어 보면 그 대답은 팽팽한 것 같습니다. 다시 말하지만, 왜 수업의 사용자들은 그들에 대해 알아야합니까? " 사용자가 반드시 "알아야 할 필요는 없습니다". 일어나야 할 일은 당신이 수업 작가의 동의없이 사람들이 수업을 확장하는 것을 막을 수 있어야한다는 것입니다. 따라서 그러한 모든 회원은 선불로 신고해야합니다. 이로 인해 사용자가 개인 회원에 대해 알게되는 것은 단지 필요한 결과 일뿐입니다.
Nicol Bolas

1
@NicolBolas 확실히. 아마 내 입장에서는 그다지 좋지 않은 표현이었습니다. 나는 그 대답이 개인적 방법의 가시성에 대한 이론적 근거를 제공하는 대신 많은 것들을 다루는 (매우 유효한) 규칙의 결과로서 개인적 방법의 가시성만을 설명한다는 것을 의미했다. 물론, 정답은 쓸모없는 것, gna은 것 및 나의 것의 조합입니다. 아직 여기에 없었던 다른 관점을 제공하고 싶습니다.
underscore_d

4

이렇게하는 데는 두 가지 이유가 있습니다.

먼저, 액세스 지정자는 컴파일러 용 이며 런타임에는 관련이 없음을 인식하십시오. 범위를 벗어난 개인 멤버에 액세스하면 컴파일 오류가 발생합니다.

간결

짧은 한두 줄의 기능을 고려하십시오. 다른 곳에서 코드의 복제를 줄이기 위해 존재합니다. 또한 알고리즘이나 다른 어떤 것이 든 많은 곳이 아닌 한 곳에서 작동하는 방식을 변경할 수 있다는 장점이 있습니다 (예 : 정렬 알고리즘 변경).

헤더에 한두 줄의 빠른 줄이 있거나 함수 프로토 타입과 어딘가에 구현이 있습니까? 헤더에서 찾기가 더 쉬우 며 짧은 기능의 경우 별도의 구현이 훨씬 더 장황합니다.

또 다른 주요 이점이 있습니다.

인라인 함수

전용 함수는 인라인 될 수 있으며, 반드시 헤더에 있어야합니다. 이걸 고려하세요:

class A {
  private:
    inline void myPrivateFunction() {
      ...
    }

  public:
    inline void somePublicFunction() {
      myPrivateFunction();
      ...
    }
};

개인 기능 공용 기능과 함께 인라인 될 수 있습니다. inline키워드는 기술적으로 제안 이며 요구 사항이 아니기 때문에 컴파일러의 재량에 따라 수행됩니다 .


클래스 본문 내에 정의 된 모든 함수는 자동으로로 표시 inline되므로 키워드를 사용할 이유가 없습니다.
Ben Voigt

@BenVoigt입니다. 나는 그것을 깨닫지 못했고 C ++ 파트 타임을 꽤 오랫동안 사용 해왔다. 그 언어는 그 같은 작은 덩어리로 나를 놀라게하는 것을 결코 멈추지 않습니다.

헤더에 메소드를 삽입해야한다고 생각하지 않습니다. 단지 동일한 컴파일 단위에 있어야합니다. 클래스에 대해 별도의 컴파일 단위를 갖는 것이 적절한 경우가 있습니까?
Samuel Danielson

@SamuelDanielson은 정확하지만 개인 함수는 클래스 정의에 있어야합니다. "개인"이라는 개념은 그것이 클래스의 일부임을 암시합니다. .cpp클래스 정의 외부에 정의 된 멤버 함수에 의해 인라인 된 파일에 비 멤버 함수를 가질 수 있지만 이러한 함수는 개인용이 아닙니다.

문제의 본문은 " 클래스 정의에서 개인 함수 를 선언 할 이유가 있습니까? 개인 함수 정의에 대해 이야기하고 있습니다. 이것은 질문에 대답하지 않습니다. @SamuelDanielson 요즘 LTO는 기능이 프로젝트의 어느 곳에서나 가능하고 인라인 될 가능성이 동일하다는 것을 의미합니다. 클래스를 여러 번역 단위로 분할하는 경우 가장 간단한 경우는 클래스가 너무 커서 의미 적으로 클래스를 여러 소스 파일로 분할하려는 것입니다. 그런 큰 수업은 권장하지 않지만 어쨌든
underscore_d

2

헤더 파일에 개인용 메소드가있는 또 다른 이유 : 공용 인라인 메소드가 하나 이상의 개인용 메소드를 호출하는 것보다 많지 않은 경우가 있습니다. 헤더에 개인용 메소드가 있다는 것은 공개 메소드에 대한 호출이 개인용 메소드의 실제 코드에 완전히 인라인 될 수 있으며 인라인이 개인용 메소드에 대한 호출로 중지되지 않음을 의미합니다. 다른 컴파일 단위에서도 (공용 메소드는 일반적 으로 다른 컴파일 단위에서 호출됩니다).

물론 개인 메소드를 포함한 모든 메소드를 모르는 경우 컴파일러가 과부하 해결과 관련된 문제점을 감지 할 수없는 이유도 있습니다.


인라인에 대한 훌륭한 포인트! 필자는 이것이 stdlib 및 인라인으로 정의 된 메소드가있는 다른 라이브러리와 특히 관련이 있다고 생각합니다. (LTO가 TU 경계를 가로 질러 거의 모든 것을 할 수있는 내부 코드가 그렇게 많지 않은 경우)
underscore_d

0

이러한 기능이 개인 구성원에게 액세스 할 수 있도록 허용합니다. 그렇지 않으면 friend어쨌든 헤더에 있어야합니다.

함수가 클래스의 개인 멤버에 액세스 할 수 있으면 private은 쓸모가 없습니다.


1
어쩌면 나는 질문에서 더 분명하게 표현해야했을 것입니다. 왜 언어가 이런 식으로 디자인 되었습니까? 왜이 디자인이 좋은가?
Praxeolitic
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.