2009-11-12 5 views
5

최근에는 구성을 위해 일반적인 setter 메서드 대신 구성 개체를 사용하는 클래스가 있습니다. 작은 예 :구성 structs 대 setters

class A { 
    int a, b; 
public: 
    A(const AConfiguration& conf) { a = conf.a; b = conf.b; } 
}; 

struct AConfiguration { int a, b; }; 

그나 :

  • 당신은 개체를 확장하고 쉽게 사용자가 지금까지 그것에 대해 알 필요없이 새로운 가치에 대한 합리적인 디폴트 값을 보장 할 수 있습니다.
  • 일관성을 위해 구성을 확인할 수 있습니다 (예 : 클래스에서 일부 값 조합 만 허용).
  • 세터를 생략하면 많은 코드를 저장할 수 있습니다.
  • Configuration 구조체의 기본 생성자를 지정하는 기본 생성자를 얻고 A(const AConfiguration& conf = AConfiguration())을 사용하십시오.

단점 (들) :

  • 당신은 건설 당시의 구성을 알 필요가 나중에 변경할 수 없습니다.

더 많은 단점이 있습니까? 그렇지 않은 경우 : 왜 더 자주 사용하지 않습니까?

답변

6

스타일의 문제이며, 사례별로 결정해야합니다.

중요한 질문은 다음과 같습니다. 개체가 작성된 후 준비되고 사용 가능하고 컴파일러가 생성자에게 필요한 모든 데이터를 전달하도록 강요합니까? 아니면 숫자 뒤에 누가 건설자가 될지를 기억해야합니다. 컴파일러 없이도 언제든지 코드를 수정해야한다는 힌트를 얻을 수 있습니다.이 있는지 여부를

A(const AConfiguration& conf) : a(conf.a), b(conf.b) {} 

또는

A(int a_, int b_) : a(a_), b(b_) {} 

중요하지 않습니다 모든 많이 그래서. (이 모든 사람들이 전자를 선호하는 매개 변수의 수는, 그러나이되는 수 - 이러한 클래스가 잘 설계되어 있는지 여부 - 논쟁의 여지가있다.) 그러나, 나는이

A a1(Configuration(42,42)); 
A a2 = Configuration(4711,4711); 
A a3(7,7); 

또는 같은 객체를 사용할 수 있는지 여부 내가 객체를 사용하기 전에이

A urgh; 
urgh.setA(13); 
urgh.setB(13); 

을해야 할, 는 큰 차이를 만들 않습니다. 특히, 누군가가 와서 다른 데이터 필드를 A에 추가하면 특히 그렇습니다.

1

이 방법을 사용하면 이진 호환성이 향상됩니다.

struct가 변경되면 (하나의 새로운 선택적 필드가 추가됨) 클래스를 사용하는 모든 코드에서 다시 컴파일해야 할 수 있습니다. 하나의 새로운 비 가상 설정 기 기능이 추가되면 이러한 재 컴파일이 필요하지 않습니다.

+0

저는 최근에 제가 지원하고있는 오래된 코드베이스로 이것을 실행했습니다. 그것은 꽤 성가신 일입니다. –

+0

Windows API는 크기 필드를 추가하고 구조체가 POD인지 확인하고 끝에 데이터 필드를 추가하는 것으로 충분합니다. 지원하는 오래된 생성자를 추가하여 새로운 구조체를 만들고 (새로운 필드에 대해 적절한 기본값을 사용하여) 호환성이 실제로 _ 증가합니다. – sbi

+0

나는 거울의 답을 썼다. –

2

주요 장점은 A 개체를 변경할 수 없다는 것입니다. ACofiguration을 실제로 사용하는 것이 생성자에 대한 a 및 b 매개 변수에 대한 이점을 제공하는지 여부는 알 수 없습니다.

4

이 방법을 사용하면 이진 호환성을보다 쉽게 ​​만들 수 있습니다.

라이브러리 버전이 변경되고 struct 구성이 포함 된 경우 생성자는 "이전"또는 "새"구성이 전달되었는지 여부를 구별하고 존재하지 않는 필드에 액세스 할 때 "액세스 위반"/ "segfault"를 피할 수 있습니다.

또한 생성자의 이름이 변경되지 않고 유지되며 서명이 변경되면 변경됩니다. 또한 바이너리 호환성을 유지할 수 있습니다.

예 : 당신이 개별적으로 또는 구조체 당 데이터를 전달하는

//version 1 
struct AConfiguration { int version; int a; AConfiguration(): version(1) {} }; 
//version 2 
struct AConfiguration { int version; int a, b; AConfiguration(): version(2) {} }; 

class A { 
    A(const AConfiguration& conf) { 
    switch (conf.version){ 
     case 1: a = conf.a; b = 0; // No access violation for old callers! 
     break; 
     case 2: a = conf.a; b = conf.b; // New callers do have b member 
     break; 
    } 
    } 
}; 
0

여기서는 감소 된 이진 호환성을 지원합니다.

내가보기에 문제는 구조체 필드에 대한 직접 액세스에서 비롯됩니다.

class AConfig1 { public: int getA() const; int getB() const; /* */ }; 
class AConfig2 { public: int getA() const; int getB(int key = 0) const; /* */ }; 

개체의 물리적 레이아웃 변화가있을 수 있습니다,하지만 내 게터하지 않았습니다과 기능에 오프셋 :

내가 b의 표현을 수정하기 때문에
struct AConfig1 { int a; int b; }; 
struct AConfig2 { int a; std::map<int,int> b; } 

, 내가 가진 반면, 나사입니다 하지 않았습니다.

물론 바이너리 호환성을 위해서는 PIMPL 관용구를 확인해야합니다. 당신이 더 많은 코드를 작성 결국 않지만

namespace details { class AConfigurationImpl; } 

class AConfiguration { 
public: 
    int getA() const; 
    int getB() const; 
private: 
    AConfigurationImpl* m_impl; 
}; 

, 당신은 당신이 기존의 것들 AFTER 보충 방법을 추가로 여기 객체의 이전 버전과의 호환성 보장이있다.

가 가

방법의 수에 의존하지 않는 메모리 인스턴스의 표현은, 그 밖에는 :

  • 가상 메소드의 유무를
  • 기본 클래스
  • 특성

VISIBLE (접근 가능한 항목이 아닌)은 무엇입니까?

그리고 여기서 우리는 속성에 변화가 없음을 보증합니다. AConfigurationImpl의 정의는 문제없이 변경 될 수 있으며 메소드의 구현도 변경 될 수 있습니다.

코드가 많을수록 생성자, 복사 생성자, 할당 연산자 및 소멸자, 물론 상당한 양의 소멸자와 물론 getters와 setter가 있습니다. 또한이 메소드는 소스 파일에 구현이 정의되어 있으므로 더 이상 인라인 될 수 없습니다.

당신의 판단에 상관없이 스스로 결정할 수 있습니다.

관련 문제