C ++의 접근 자 메서드 (getter 및 setter)에 대한 규칙


78

C ++의 접근 자 메서드에 대한 몇 가지 질문이 SO에서 요청되었지만 문제에 대한 호기심을 충족시킬 수있는 사람은 없습니다.

Stroustrup과 다른 유명한 프로그래머처럼 접근자가 많은 클래스를 나쁜 OO의 징후로 간주하기 때문에 가능한 한 접근자를 피하려고합니다. C ++에서는 대부분의 경우 클래스에 더 많은 책임을 추가하거나이를 피하기 위해 friend 키워드를 사용할 수 있습니다. 그러나 어떤 경우에는 특정 클래스 멤버에 대한 액세스 권한이 정말로 필요합니다.

몇 가지 가능성이 있습니다.

1. 접근자를 전혀 사용하지 마십시오

각 멤버 변수를 공개 할 수 있습니다. 이것은 Java에서는 실행되지 않지만 C ++ 커뮤니티에서는 괜찮은 것 같습니다. 그러나 객체에 대한 명시 적 복사 또는 읽기 전용 (const) 참조가 반환되어야하는 경우에 대해 약간 걱정됩니다. 과장된 것입니까?

2. 자바 스타일의 get / set 메소드 사용

Java에서 온 것인지 확실하지 않지만 이것은 의미합니다.

int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount

3. 객관적인 C 스타일 get / set 메서드 사용

이것은 약간 이상하지만 점점 더 일반적입니다.

int amount(); // Returns the amount
void amount(int amount); // Sets the amount

작동하려면 멤버 변수에 대해 다른 이름을 찾아야합니다. 어떤 사람들은 밑줄을 추가하고 다른 사람들은 "m_"앞에 추가합니다. 나도 좋아하지 않습니다.

어떤 스타일을 사용하고 왜 사용합니까?


5
작은 데이터 전용 구조체를 제외하고 C ++에서 공용 멤버 변수가 괜찮을 것이라는 인상을 준 것은 무엇입니까? 보통은 안돼.
Georg Fritzsche

1
예, C ++에서도 사용할 수 없습니다. 그러나 MFC와 함께 일한다면 괜찮을 것이라는 가정을 얻을 수 있습니다.
vobject

가장 최근에 [set / get methods in C ++] ( stackoverflow.com/questions/3632533 ) 로 논의되었으며 , serach "c ++ getter setter" 에서 발견했습니다 . [Getter 및 setter, 포인터 또는 참조 및 C ++에서 사용하기에 좋은 구문? ] ( stackoverflow.com/questions/1596432 ), C ++ getter / setters 코딩 스타일 및 기타.
dmckee --- 전 중재자 새끼 고양이

4
getter / setter가 잘못된 이유는 이 기사 (PDF) 를 참조하십시오 .
sbi

1
@vines : 실제 OO에서 객체의 인터페이스 (정적으로 형식화 된 언어로 클래스에 정의 됨)는 객체의 상태와 관련해서는 안되며 객체가 제공하는 작업에만 관련됩니다. 이러한 작업이 객체의 상태를 변경하는 것은 구현 세부 사항입니다.
sbi

답변:


44

유지 관리 관점에서 4 백만 줄의 C ++ 코드 (단 하나의 프로젝트에 불과 함)가있는 내 관점에서는 다음과 같이 말할 것입니다.

  • 멤버가 변경 불가능 const하거나 (즉 ) 종속성이없는 단순 (멤버 X 및 Y가있는 포인트 클래스) 인 경우 getter / setter를 사용하지 않는 것이 좋습니다 .

  • 회원 private만 있으면 게터 / 세터를 건너 뛰어도 괜찮습니다. 또한 .cpp 단위가 작은 것처럼 내부 pimpl 클래스의 멤버를 계산 private합니다.

  • 멤버가 publicor protected( protected만큼 나쁘고 public) const비-단순하지 않거나 종속성이있는 경우 getter / setter를 사용하십시오.

유지 보수 담당자로서 게터 / 세터를 갖고 싶어하는 주된 이유는 브레이크 포인트 / 로깅 / 다른 것을 넣을 장소가 있기 때문입니다.

검색 가능성이 더 높기 때문에 대안 2 스타일을 선호합니다 (유지 관리 가능한 코드 작성의 핵심 구성 요소).


2
커뮤니티 위키 질문에 대한 특정 답변을 수락하는 것은 모든 답변이 도움이되기 때문에 항상 조금 이상합니다. 그러나 나는 당신의 것이 가장 정당하다고 생각합니다. 나는 현재 가능한 한 getter / setter 메서드를 피하려고 노력하고 있습니다. 대안을 찾는 데 시간이 좀 걸리지 만 대체로 더 좋습니다. 언급 한대로 간단한 데이터가 필요한 경우 일반적으로 데이터 전용 구조체를 사용합니다.
Noarth 2010 년

접근자를 디버그해야하는 경우 사용하지 않는 또 다른 이유입니다. 확실히 중단 점을 설정할 더 흥미로운 장소가 있습니다.
hoodaticus

1
이것은 디버깅 측면에서 유용하다는 것을 보여주는 좋은 점이지만, 해당 정보를 얻을 수있는 다른 방법이 있다는 데 동의합니다. 그 외에는 내 생각이 GETS & SETS와 함께 사용되지 않습니다. 많은 사람들이 GET / SET을 사용하여 반환 값을 수정할 수 있다고 제안하지만 GET / SET이 아니라 일부 작업 집합을 수행하는 메서드이므로 당면한 프로세스에 대한 완전한 의도로 이름을 지정해야합니다.
Jeremy Trifilo

9

2) 귀하의 의도를 가장 명확하게하기 때문에 최고의 IMO입니다. set_amount(10)보다 의미 amount(10)있고 좋은 부작용으로 amount.

캡슐화가 없기 때문에 공용 변수는 일반적 으로 나쁜 생각입니다. 변수가 업데이트 될 때 캐시를 업데이트하거나 창을 새로 고쳐야한다고 가정합니까? 변수가 공용이면 너무 나쁩니다. 설정된 방법이 있으면 거기에 추가 할 수 있습니다.


2
표준 캡슐화는 논리 유닛의 내부 상태 및 동작 세부 정보를 숨기는 것을 의미합니다. 공용 인터페이스를 통해 개인 데이터 멤버에 액세스하는 경우 접근 자 와 함께 수행한다는 사실에 관계없이 이미 캡슐화 중단됩니다. 잘 캡슐화 된 클래스에는 접근자가 없습니다. 대신 공개적으로 보이지 않는 방식으로 내부 상태를 사용하고 영향을 미치는 활성 메서드가 있습니다.
ulidtko

1
"변경된 요구 사항"시나리오 (예 : 캐시 변경 또는 UI 업데이트 또는 getId()호출 내에서 네트워크 요청 보내기)와 관련하여 해당 논리를 getter / setter에 채우면 접근 자 메서드의 계약을 위반하게됩니다. 이런 식으로 클라이언트 코드 컴파일을 중단하지는 않지만 런타임 작업을 중단하게되며 이는 훨씬 더 나쁩니다. 정상적인 상태에있는 사람은 getter가 가끔 메모리 할당을 수행 할 수 있거나 setter가 파일 시스템에 쓸 수 있다고 가정하지 않기 때문입니다. 이러한 모든 시나리오 에서 새로운 요구 사항에 맞게 재 설계 해야 합니다.
ulidtko 2012 년

# 1은 # 2가 아니라 당신의 의도를 가장 명확하게합니다. # 2는 변수를 비공개로 만들어 내부 상태를 캡슐화하는 것처럼 가장 한 다음, 공개 접근자를 통해 누구나 해당 변수를 읽고 쓸 수 있도록 허용하여 해당 상태를 숨기는 모든 작업을 취소합니다. # 2는 분명한 의도가 아닌 쉘 게임 일뿐입니다.
hoodaticus

7
  1. 나는이 스타일을 사용하지 않는다. 클래스 디자인의 미래를 제한 할 수 있고 명시 적 geter 또는 setter는 좋은 컴파일러와 마찬가지로 효율적입니다.

    물론 실제로 인라인 명시 적 getter 또는 setter는 클래스 구현에 대한 기본 종속성을 만듭니다. 의미 론적 종속성을 줄입니다. 변경하면 여전히 모든 것을 다시 컴파일해야합니다.

  2. 접근 자 메서드를 사용할 때의 기본 스타일입니다.

  3. 이 스타일은 나에게 너무 영리 해 보인다. 드물게 사용하지만 접근자가 가능한 한 변수처럼 느껴지기를 원하는 경우에만 사용합니다.

나는 그들이 모두 정상으로 초기화되었는지 확인하기 위해 생성자를 가진 간단한 변수 가방에 대한 경우가 있다고 생각합니다. 이 작업을 수행 할 때 간단히 a로 struct만들고 모두 공개합니다.


4
"너무 영리한"경우 +1. 오버로드 가능성을 남용하지 않는 것이 좋습니다. 이는 독자들에게 더 어렵고 코드베이스 검색도 더 어렵습니다.
Matthieu M.

-1 "클래스 디자인 등의 미래 제한". 지금 그대로 클래스를 디자인하고 있습니다. 나중에 다시 디자인하려면 그렇게하십시오. 그 분야가 미래에 사라지면 어떨까요? 접근자는 당신을 돕지 않을 것입니다. 그리고 당신이 그것을 지키고, 약간의 쓰레기 가치를 반환한다면, 당신은 당신이 알지 못하는 침묵의 파손을 겪을 수 있습니다. 과도하게 설계하지 마십시오. 그렇지 않으면 필드 이름-값 접근 자의 맵을 가지고이를 수행 할 수 있습니다.
einpoklum

"접근자가 가능한 한 변수처럼 느끼도록"원하는 이유는 무엇입니까? 그것들은 변수가 아닙니다. 왜 누군가가 자신이 있다고 생각하도록 혼동하고 싶습니까?
deetz

@deetz-글쎄요, @ 속성은 파이썬에서 꽤 유명해 보입니다. 이것이 보이지 않게 변수 액세스를 함수 호출로 변환하는 방법입니다.
Omnifarious

6
  1. pure데이터 를 표현하고 싶다면 좋은 스타일 입니다.

  2. 나는 그것을 좋아하지 않는다 :) get_/set_우리가 C ++에서 그것들을 오버로드 할 수있을 때 정말로 불필요 하기 때문 이다.

  3. STL과 같은이 스타일을 사용 std::streamString::str하고 std::ios_base::flags그것을 피해야하는 경우를 제외하고! 언제? 다른 유형의 이름을 가진 메소드의 이름이 충돌 후 때 get_/set_스타일과 같은 사용 std::string::get_allocator의 때문에 std::allocator.


4
나는 STL이 그 점에서 약간 일치하지 않을 수 있다고 두려워합니다. 슬프게도 Stroustrup 자신은 getter / setters를 수행하는 방법에 대한 의견을 표명하지 않은 것 같습니다. 그가 많은 수업을 싫어한다는 점을 제외하고는 말입니다.
Noarth

4

일반적으로 시스템의 너무 많은 엔티티에서 너무 많은 게터와 세터를 사용하는 것은 좋지 않다고 생각합니다. 이는 잘못된 설계 또는 잘못된 캡슐화의 표시 일뿐입니다.

하지만 그러한 디자인을 리팩토링해야하고 소스 코드를 사용할 수있는 경우 방문자 디자인 패턴을 사용하는 것이 좋습니다. 그 이유는:

ㅏ. 클래스에게 개인 상태에 대한 액세스를 허용 할 사람을 결정할 수있는 기회를 제공합니다.

비. 개인 상태에 관심이있는 각 엔터티에게 허용 할 액세스 권한을 클래스에게 결정할 수있는 기회를 제공합니다.

씨. 명확한 클래스 인터페이스를 통해 이러한 외부 액세스를 명확하게 문서화합니다.

기본 아이디어는 다음과 같습니다.

a) 가능하면 재 설계하십시오.

b) 다음과 같은 리팩터링

  1. 클래스 상태에 대한 모든 액세스는 잘 알려진 개별 인터페이스 를 통해 이루어집니다.

  2. 예를 들어 외부 엔티티 GOOD의 모든 액세스가 허용되어야하고, 외부 엔티티 BAD의 모든 액세스 가 허용되지 않아야하며, 외부 엔티티 OK 가 가져 오도록 허용되어야하는 등 각 인터페이스에 대해 일종의해야 할 일과하지 말아야 할 일을 구성 할 수 있어야합니다. 설정되지 않음 (예 :)


참으로 흥미로운 점은 이것이 왜 투표가 거부되었는지 전혀 모릅니다. 하지만 특정 클래스가 특정 멤버에 액세스해야하는 경우 친구 키워드를 선호한다고 생각합니다.
fhd

2
  1. 나는 접근자를 사용에서 제외하지 않을 것입니다. 일부 POD 구조에 대해서는 가능하지만 좋은 것으로 생각합니다 (일부 접근 자도 추가 논리를 가질 수 있음).

  2. 코드에서 일관성이 있다면 명명 규칙은 실제로 중요하지 않습니다. 여러 타사 라이브러리를 사용하는 경우 어쨌든 다른 이름 지정 규칙을 사용할 수 있습니다. 그래서 그것은 맛의 문제입니다.


1

추가 가능성은 다음과 같습니다.

int& amount();

추천 할 수 있을지 모르겠지만, 특이한 표기법이 사용자가 데이터를 수정하지 못하도록 할 수 있다는 장점이 있습니다.

str.length() = 5; // Ok string is a very bad example :)

때로는 다음과 같은 좋은 선택 일 수 있습니다.

image(point) = 255;  

또 다른 가능성은 기능적 표기법을 사용하여 객체를 수정하는 것입니다.

edit::change_amount(obj, val)

이렇게하면 위험 / 편집 기능을 자체 문서와 함께 별도의 네임 스페이스에서 제거 할 수 있습니다. 이것은 일반적인 프로그래밍과 함께 자연스럽게 나오는 것 같습니다.


나는 이것을 찬성하지 않았지만 첫 번째는 일반 접근자가 숨겨둔 디자인 세부 사항을 노출하기 때문에 매우 좋지 않은 대답입니다. 그리고 다른 제안은 답을 되찾기에 충분하지 않습니다. 첫 번째 스타일과 같은 것을 시도하려는 경우 최소한 operator =내부 데이터 멤버에 대한 참조 대신에있는 개체를 반환 합니다.
Omnifarious

일을 명확히하기 위해 내 대답은 OP가 시작한 가능성 목록 만 완성합니다 (아마도 의견이 있어야 함). 나는 특히 그러한 가능성을 권장하지 않습니다. Omnifarious가 말했듯 operator=이 첫 번째 경우에 프록시 클래스 오버로딩 을 사용하는 것은 분명히 최소한해야 할 좋은 일입니다. 클래스 계층 구조를 가로 지르는 편집 기능을 제공하고이 기능을 클래스에서 명확하게 분리 할 수있는 경우 두 번째 기능을 사용합니다. 하지만 단순한 세터에게는 나쁜 생각 일 것입니다.
log0

1

의미있는 데이터를 참조하기 위해 정수 유형 대신 클래스의 이상화를 보았습니다.

아래와 같은 것은 일반적으로 C ++ 속성을 잘 사용하지 않습니다.

struct particle {
    float mass;
    float acceleration;
    float velocity;
} p;

왜? p.mass * p.acceleration의 결과는 예상대로 강제가 아닌 부동 소수점이기 때문입니다.

목적을 지정하기위한 클래스의 정의 ( 앞에서 언급 한 금액 과 같은 값이더라도 )가 더 합리적이며 다음과 같은 작업을 수행 할 수 있습니다.

struct amount
{
    int value;

    amount() : value( 0 ) {}
    amount( int value0 ) : value( value0 ) {}
    operator int()& { return value; }
    operator int()const& { return value; }
    amount& operator = ( int const newvalue )
    {
        value = newvalue;
        return *this;
    }
};

int 연산자에 의해 암시 적으로 amount 값에 액세스 할 수 있습니다. 더욱이:

struct wage
{
    amount balance;

    operator amount()& { return balance; }
    operator amount()const& { return balance; }
    wage& operator = ( amount const&  newbalance )
    {
        balance = newbalance;
        return *this;
    }
};

Getter / Setter 사용 :

void wage_test()
{
    wage worker;
    (amount&)worker = 100; // if you like this, can remove = operator
    worker = amount(105);  // an alternative if the first one is too weird
    int value = (amount)worker; // getting amount is more clear
}

이것은 다른 접근 방식이며 좋거나 나쁘다는 것을 의미하지는 않지만 다릅니다.


0

가장 간결 해 보이는 또 다른 가능성에 대해 말씀 드리겠습니다.

읽고 수정해야 함

해당 변수를 public으로 선언하기 만하면됩니다.

class Worker {
public:
    int wage = 5000;
}

worker.wage = 8000;
cout << worker.wage << endl;

읽기만 필요

class Worker {
    int _wage = 5000;
public:
    inline int wage() {
        return _wage;
    }
}

worker.wage = 8000; // error !!
cout << worker.wage() << endl;

이 접근 방식의 단점은 액세스 패턴을 변경하고자 할 때 모든 호출 코드를 변경 (즉, 괄호 추가)해야한다는 것입니다.


0

# 3의 변형, '유창한'스타일이 될 수 있다고 들었습니다.

class foo {
  private: int bar;
  private: int narf;
  public: foo & bar(int);
  public: int bar();
  public: foo & narf(int);
  public: int narf();
};

//multi set (get is as expected)
foo f; f.bar(2).narf(3);

기능면에서는 괜찮지 만 질문의 범위를 벗어납니다. 이것은 기본적으로 집합을 연결하는 기능이있는 C # 스타일 getter / setter입니다. 이 패턴이 자주 사용되지 않는다고 들었고 확실히 쉽게 교체 할 수 있다는 것을 알고 있습니다. 이 질문은 또한 당신이 관심을 가지고 있다면 다른 패턴보다이 패턴을 사용 하는 이유 에 대한 설명을 요구합니다 .
Matias Chara
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.