보호 된 멤버 변수를 사용해야합니까?


97

보호 된 멤버 변수를 사용해야합니까? 장점은 무엇이며 이로 인해 어떤 문제가 발생할 수 있습니까?

답변:


74

보호 된 멤버 변수를 사용해야합니까?

상태를 숨기는 것에 대해 얼마나 까다로운 지에 따라 다릅니다.

  • 내부 상태의 유출을 원하지 않는 경우 모든 멤버 변수를 private으로 선언하는 것이 좋습니다.
  • 하위 클래스가 내부 상태에 액세스 할 수 있다는 사실에 관심이 없다면 protected로 충분합니다.

개발자가 함께 와서 클래스를 하위 클래스로 지정하면 완전히 이해하지 못하기 때문에 엉망이 될 수 있습니다. 공용 인터페이스가 아닌 개인 멤버를 사용하면 작업이 수행되는 방식에 대한 구현 특정 세부 사항을 볼 수 없으므로 나중에 변경할 수있는 유연성을 제공합니다.


1
get / set 메서드를 사용하여 보호 된 변수와 개인용 변수의 성능에 대해 언급 할 수 있습니까?
Jake

3
프로파일 링을 통해 병목이 접근 자 (거의 결코 그렇지 않음)가된다는 것을 알지 못한다면 걱정할 가치가 없다고 말하고 싶습니다. 문제가 될 경우 JIT를 더 현명하게 만들 수있는 트릭이 있습니다. 예를 들어 Java에서는 접근자를 최종으로 표시하여 인라인 될 수 있음을 힌트 할 수 있습니다. 솔직히 게터와 세터의 성능은 시스템 구성과 프로파일 러가 결정한 실제 성능 문제를 다루는 것보다 훨씬 덜 중요합니다.
Allain Lalonde

23
@Jake : 성능 가정을 기반으로 디자인 결정을 내리면 안됩니다. 최고의 디자인이라고 생각하는 것을 기반으로 디자인 결정을 내리고 실제 프로파일 링에서 디자인에 병목 현상이 나타나는 경우에만 가서 수정합니다. 일반적으로 디자인이 건전하면 성능도 좋습니다.
Mecki

공용 인터페이스가 아닌 개인 멤버를 사용하면 구현 특정 세부 정보를 볼 수 없습니다 . 클래스를 열고 조회 할 수 있으므로 말이되지 않습니까?!
Black

2
@Black Clearly Allain은 해당 멤버에 ' 액세스 할 수 없으므로 '해당 멤버에 대해 코드를 작성할 수 없으므로 클래스 작성자가 나중에 보호 된 멤버를 자유롭게 제거 / 변경할 수 있음을 의미했습니다. (물론 pimpl 관용구를 사용하면 헤더를 포함한 번역 단위에서 시각적으로 숨길 수 있습니다.)
underscore_d

31

요즘 일반적인 느낌은 파생 클래스와 그 기반 사이에 과도한 결합을 유발한다는 것입니다.

보호 된 메서드 / 속성에 비해 특별한 이점이 없으며 (한때 약간의 성능 이점이있을 수 있음), 매우 깊은 상속이 유행하던 시대에 더 많이 사용되었지만 현재는 그렇지 않습니다.


2
no particular advantage over protected methods/propertiesno particular advantage over *private* methods/properties?
Penghe Geng 2014

아니요, 파생 클래스와 해당 기반 간의 다양한 통신 방법의 장점 / 단점에 대해 이야기하고 있었기 때문에 이러한 모든 기술은 '보호'될 것입니다. 차이점은 멤버 변수 (필드)인지 속성 / 메소드 ( 즉, 어떤 종류의 서브 루틴).
Will Dean

1
빠른 설명에 감사드립니다. 6 년 된 게시물에 대한 내 질문에 대한 원래 포스터의 답변을 한 시간 안에 받게되어 기쁩니다. 대부분의 다른 온라인 포럼에서는 이런 일이 일어날 수 없다고 생각합니다. :)
Penghe Geng 2014

9
더 놀라운 아직도 내가 실제로 ... 시간의 양을 통해 자신에 동의한다는 것입니다
윌 딘

생성자의 업무 순서 중 하나는 모든 상태 변수가 명시 적으로 초기화되었는지 확인하는 것입니다. 이 규칙을 준수하면 super구성을 사용 하여 부모 생성자를 호출 할 수 있습니다 . 그런 다음 부모 클래스에서 개인 상태 변수를 초기화합니다.
ncmathsadist

31

일반적으로 의도적으로 공개 된 것으로 간주되지 않은 내용은 비공개로 설정합니다.

파생 클래스에서 해당 개인 변수 또는 메서드에 액세스해야하는 상황이 발생하면 개인에서 보호로 변경합니다.

이것은 거의 발생하지 않습니다. 대부분의 상황을 모델링하는 데 특히 좋은 방법이 아니기 때문에 저는 상속의 팬이 아닙니다. 어쨌든 계속해서 걱정하지 마십시오.

나는 이것이 대부분의 개발자들에게 괜찮다고 말하고 싶습니다 (그리고 아마도 그것에 대해 최선의 방법 일 것입니다).

문제의 간단한 사실은 다른 개발자가 1 년 후에 와서 개인 멤버 변수에 대한 액세스가 필요하다고 결정하면 코드를 편집하고 보호 된 것으로 변경하고 비즈니스를 계속할 것입니다.

이에 대한 유일한 예외는 바이너리 dll을 블랙 박스 형태로 제 3 자에게 배송하는 사업에있는 경우입니다. 이것은 기본적으로 Microsoft, 'Custom DataGrid Control'공급 업체 및 확장 성 라이브러리와 함께 제공되는 몇 가지 다른 대형 앱으로 구성됩니다. 당신이 그 범주에 속하지 않는 한, 이런 종류의 것에 대해 걱정하는 데 시간 / 노력을 소비 할 가치가 없습니다.


8

나에게 중요한 문제는 일단 변수를 보호 하면 하위 클래스가 항상 범위를 벗어날 수 있기 때문에 클래스의 어떤 메서드도 범위 내에있는 값에 의존 하도록 허용 할 수 없다는 것입니다.

예를 들어 렌더링 가능한 객체의 너비와 높이를 정의하는 클래스가 있고 해당 변수를 보호하는 경우 가로 세로 비율 (예 : 가로 세로 비율)에 대한 가정을 할 수 없습니다.

비판적으로 저는 코드가 라이브러리로 출시 된 순간부터 이러한 가정을 할 수 없습니다. 종횡비를 유지하기 위해 setter를 업데이트하더라도 변수가 setter를 통해 설정되거나 기존 코드의 게터.

내 클래스의 어떤 하위 클래스도 변수 값을 적용 할 수 없기 때문에 보장하도록 선택할 수 없습니다. 그 자신의 서브 클래스의 전체 지점의 경우에도 .

예로서:

  • 너비와 높이가 보호 된 변수로 저장되는 사각형 클래스가 있습니다.
  • 내 컨텍스트 내에서 명백한 하위 클래스는 "DisplayedRectangle"클래스입니다. 유일한 차이점은 그래픽 디스플레이에 대해 너비와 높이를 유효한 값으로 제한한다는 것입니다.
  • 하지만 지금은 불가능합니다. 내 DisplayedRectangle 클래스 해당 값을 진정으로 제한 할 수 없기 때문에 하위 클래스가 DisplayedRectangle로 처리되는 동안 값을 직접 재정의 할 수 있기 때문입니다.

변수를 비공개로 제한하면 setter 또는 getter를 통해 원하는 동작을 적용 할 수 있습니다.


7

일반적으로 보호 된 멤버 변수를 사용하는 코드를 완전히 제어 할 수있는 드문 경우로 유지합니다. 공용 API를 만드는 경우 절대로하지 않습니다. 아래에서 멤버 변수를 개체의 "속성"이라고합니다.

다음 은 private-with-accessor가 아닌 멤버 변수를 보호 한 후에 슈퍼 클래스가 할 수없는 작업입니다.

  1. 속성을 읽을 때 즉시 값을 느리게 만듭니다. 보호 된 getter 메서드를 추가하면 값을 느리게 생성하고 다시 전달할 수 있습니다.

  2. 속성이 수정되거나 삭제 된시기를 알 수 있습니다. 수퍼 클래스가 해당 변수의 상태에 대해 가정 할 때 버그가 발생할 수 있습니다. 변수에 대해 보호 된 setter 메서드를 만들면 해당 제어가 유지됩니다.

  3. 변수를 읽거나 쓸 때 중단 점을 설정하거나 디버그 출력을 추가합니다.

  4. 해당 멤버 변수를 사용할 수있는 모든 코드를 검색하지 않고 이름을 바꿉니다.

일반적으로 보호 된 멤버 변수를 만들 것을 권장하는 드문 경우라고 생각합니다. 몇 시간 후에 보호 된 변수를 수정 한 다른 코드에서 버그를 추적하는 것보다 게터 / 세터를 통해 속성을 노출하는 데 몇 분을 보내는 것이 좋습니다. 뿐만 아니라 종속 코드를 손상시키지 않고 향후 기능 (예 : 지연로드)을 추가하지 않도록 보장합니다. 지금하는 것보다 늦게하는 것이 더 어렵습니다.


7

디자인 수준에서는 보호 된 속성을 사용하는 것이 적절할 수 있지만 구현을 위해 접근 자 / 변이 자 메서드가 아닌 보호 된 멤버 변수에 매핑하는 이점이 없습니다.

보호 된 멤버 변수는 기본 클래스 클래스의 내부 상태에 대한 클라이언트 코드 (하위 클래스) 액세스를 효과적으로 허용하기 때문에 상당한 단점이 있습니다. 이것은 기본 클래스가 불변성을 효과적으로 유지하는 것을 방지합니다.

같은 이유로 보호 된 멤버 변수는 상수가 보장되거나 단일 스레드로 제한되지 않는 한 안전한 다중 스레드 코드 작성을 훨씬 더 어렵게 만듭니다.

접근 자 / 변이 자 메서드는 유지 관리 중에 훨씬 더 많은 API 안정성과 구현 유연성을 제공합니다.

또한 OO 순수 주의자라면 객체는 상태를 읽거나 설정하지 않고 메시지를 보내 공동 작업 / 통신합니다.

그 대가로 그들은 거의 장점을 제공하지 않습니다. 다른 사람의 코드에서 반드시 제거하지는 않지만 직접 사용하지는 않습니다.


4

대부분의 경우 protected를 사용하는 것은 잘못 설계된 파생 클래스에 의해 잘 분해 될 수있는 클래스의 캡슐화를 다소 깨뜨리기 때문에 위험합니다.

하지만 좋은 예가 하나 있습니다. 일종의 일반 컨테이너를 사용할 수 있다고 가정 해 보겠습니다. 내부 구현과 내부 접근자가 있습니다. 그러나 데이터에 대한 공개 액세스 권한을지도, hash_map, 벡터와 같이 3 개 이상 제공해야합니다. 그러면 다음과 같은 것이 있습니다.

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

나는 이런 종류의 코드를 한 달 전에 사용했습니다 (따라서 코드는 메모리에서 가져온 것입니다). 몇 가지 생각을 한 후, 제네릭 Base 컨테이너는 추상 클래스 여야한다고 생각합니다. 비록 그것이 꽤 잘 살 수 있더라도 Base를 직접 사용하는 것은 매우 고통 스러울 것이기 때문에 금지되어야합니다.

요약 따라서 파생 클래스에서 사용하는 데이터를 보호했습니다. 그럼에도 불구하고 우리는 Base 클래스가 추상적이어야한다는 사실을 고려해야합니다.


공개 멤버보다 더 이상 캡슐화를 중단하지 않습니다. 파생 클래스가 클래스 사용자에게 노출되지 않은 클래스 상태를 사용할 수 있다는 설정입니다.
gbjbaanb

@gbjbaanb : "퍼블릭 멤버가하는 것보다 더 이상 캡슐화를 중단하지 않습니다"가 "[유일한] 파생 클래스가 클래스의 상태를 사용할 수 있음"과 다르다는 사실이 모순됩니다. "protected"는 공개와 비공개의 중간입니다. 그래서 "보호 된 [...] 다소 캡슐화를 끊습니다"는 여전히 사실입니다 ...
paercebal

실제로 C ++ 언어에서 std :: stack과 같은 컨테이너 어댑터는 "c"라는 보호 변수를 사용하여 기본 컨테이너 개체를 노출합니다.
Johannes Schaub-litb

나는이 포스트가 꽤 오래되었다는 것을 알고있다. 그러나 나는 차임 할 필요성을 느낀다. 당신은 캡슐화를 "어느 정도"깨뜨리지 않고 완전히 깨뜨린 다. protected보다 더 이상 캡슐화되지 않습니다 public. 나는 기꺼이 틀렸다는 것을 증명할 것입니다. 당신이해야 할 일은 보호 된 멤버로 클래스를 작성하고 내가 그것을 수정하는 것을 금지하는 것입니다. protected 사용의 전체 요점은 상속을위한 것이기 때문에 클래스는 최종적이지 않아야합니다. 어떤 것이 캡슐화되어 있거나 그렇지 않습니다. 중간 상태가 없습니다.
태칸

3

요컨대, 그렇습니다.

보호 된 멤버 변수를 사용하면 동일한 패키지의 모든 클래스뿐 아니라 모든 하위 클래스에서 변수에 액세스 할 수 있습니다. 이것은 특히 읽기 전용 데이터에 매우 유용 할 수 있습니다. 그러나 보호 된 멤버 변수의 사용은 개인 멤버 변수와 몇 개의 getter 및 setter를 사용하여 복제 될 수 있기 때문에 이것이 필요하다고 생각하지 않습니다.


1
반대로 private 멤버 변수도 필요하지 않습니다. public은 모든 용도로 충분합니다.
Alice

3

기록을 위해 "Exceptional C ++"의 항목 24 아래 각주 중 하나에서 Sutter는 "공개 또는 보호 된 멤버 변수가있는 클래스를 작성하지 않을 것입니다. 맞습니까? (일부 라이브러리에서 설정 한 좋지 않은 예제와 관계없이) .) "


2

.Net 액세스 수정 자에 대한 자세한 정보는 여기로 이동하십시오.

보호 된 멤버 변수에는 실제 장점이나 단점이 없으며 특정 상황에서 필요한 것이 무엇인지에 대한 질문입니다. 일반적으로 멤버 변수를 비공개로 선언하고 속성을 통해 외부 액세스를 활성화하는 것이 허용됩니다. 또한 일부 도구 (예 : 일부 O / R 매퍼)는 객체 데이터가 속성으로 표시 될 것으로 예상하고 공용 또는 보호 된 멤버 변수를 인식하지 못합니다. 그러나 서브 클래스 (및 서브 클래스 만)가 특정 변수에 액세스하기를 원한다는 것을 알고 있다면이를 보호 된 것으로 선언하지 않을 이유가 없습니다.


서브 클래스를 원하는 액세스 변수 것은 그들을 자유롭게 할 수 있도록하고자 매우 다른 변이 를. 이것이 보호 된 변수에 대한 주요 인수 중 하나입니다. 이제 기본 클래스는 모든 파생 클래스가 보호 된 멤버로 모든 작업을 수행 할 수 있기 때문에 불변성이 있다고 가정 할 수 없습니다. 그것이 그들에 대한 주요 논쟁입니다. 그들이 데이터 에 액세스 해야한다면 ... 접근 자를 작성하세요. : P (나는 보호 된 변수를 사용하지만 아마도 내가해야하는 것보다 더 많이 사용하고, 줄이려고 노력할 것입니다!)
underscore_d
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.