클래스 상수를 데이터 구성원이나 방법에 저장하는 것이 더 낫습니까?

StackOverflow https://stackoverflow.com/questions/647648

  •  22-07-2019
  •  | 
  •  

문제

나는 최근에 B- 스플라인 곡선을 렌더링하는 수업을 썼습니다. 이러한 곡선은 여러 제어점으로 정의됩니다. 원래 8 개의 제어점을 사용하려고 했으므로 다음과 같이 수업에 상수를 추가했습니다.

class Curve
{
   public:
      static const int CONTROL_POINT_COUNT = 8;
};

이제 임의의 양의 제어 지점을 허용하기 위해이 클래스를 연장하고 싶습니다. 그래서 나는 이것을 다음으로 바꾸고 싶습니다.

class Curve
{
   public:
      int getControlPointCount() {return _controlPointCount;}
};

문제는 적응성을 용이하게하기 위해 상수를 시작하는 방법에 상수를 저장하는 것이 더 좋지 않은지 여부입니다. 다시 말해, 따라서 시작한 것이 더 낫지 않습니까 :

class Curve
{
   public:
      int getControlPointCount() {return 8;}
};

이것의 장점은 상수를 주위로 이동하는 대신 문제의 방법에서 하나의 기호를 변경할 수 있다는 것입니다.

이것은 좋은 연습입니까, 나쁜 관행입니까?

도움이 되었습니까?

해결책

일반적으로 가능한 적은 커플 링을 가능한 한 수동으로 유지하는 것이 좋습니다.

곡선의 제어 지점의 수는 곡선의 제어점 수입니다. 마음대로 설정할 수있는 독립 변수가 아닙니다.

그래서 나는 보통 const 표준 컨테이너 참조를 노출시킬 것입니다.

class Curve
{   
    private:
        std::vector<Point>& _controlPoints;

    public:      
        Curve ( const std::vector<Point>& controlPoints) : _controlPoints(controlPoints)
        {
        }

        const std::vector<Point>& getControlPoints ()
        {
            return _controlPoints;
        }
};

그리고 제어점이 얼마나 많은지 알고 싶다면 사용하십시오. curve.getControlPoints().size(). 대부분의 사용 사례에서 어쨌든 포인트와 카운트를 원한다고 생각하고 표준 컨테이너를 노출하면 표준 라이브러리의 반복자 관용구 및 내장 알고리즘을 사용할 수 있으며 오히려 카운트를 얻고 전화 할 수 있습니다. 같은 기능 getControlPointWithIndex 루프에서.

곡선 클래스에 다른 것이 없다면, 나는 다음과 같이 갈 수도 있습니다.

typedef std::vector<Point> Curve;

(렌더러 클래스가 렌더링 파이프 라인에 대한 세부 정보를 가질 수 있기 때문에 종종 곡선이 렌더링되지 않습니다.

다른 팁

int getControlPointCount() {return _controlPointCount;}

이것은 액세서입니다. Litb가 지적했듯이 액세서를 위해 Const 정적을 교환하는 것은 실제로 이득이 아닙니다. 당신이 정말로 필요한 것 미래 방지 아마도 액세서와 돌연변이터 일 것입니다.

int getControlPointCount() {return _controlPointCount;} // accessor

또한 액세서를위한 디자인을 던져서 다음을 만들었습니다.

int getControlPointCount() const {return _controlPointCount;} // accessor

그리고 해당 :

void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator

이제 정적 객체와의 큰 차이점은 제어점 카운트가 더 이상 클래스 수준 속성이 아니라 인스턴스 레벨 1이라는 것입니다. 이것은 디자인 변경. 이런 식으로 원하십니까?

알: 클래스 레벨 정적 카운트입니다 public 따라서 액세서가 필요하지 않습니다.

질문에 더 잘 대답하려면 ControlPointCount 변수가 어떻게 설정되어 있는지 알아야합니다. 수업에서 밖에 설정되어 있습니까? 이 경우 세터도 정의해야합니다. 아니면 곡선 클래스가 유일한 설정을 담당합니까? 컴파일 시간 또는 런타임에만 설정됩니까?

어쨌든,이 형태로도 마법 번호를 피하십시오.

int getControlPointCount() {return 8;}

이게 낫다:

int getControlPointCount() {return CONTROL_POINT_COUNT;}

메소드는 클래스의 외부에 영향을 미치지 않고 내부 구현 (상수 값을 사용하고 구성 파일을 읽고 동적으로 값 변경)을 수정할 수 있다는 이점이 있습니다.

class Curve
{   
    private:
        int _controlPointCount;

        void setControlPointCount(int cpc_arg)
        {
            _controlPointCount = cpc_arg;
        }

    public:      
        curve()
        {
            _controlPointCount = 8;
        }

        int getControlPointCount() const
        {
            return _controlPointCount;
        }
};

다음과 같은 코드를 비공개로 사용하여 다음 단계로 이동할 때까지 본체가 제어점 수로 재생할 수 없도록 코드를 만들 것입니다. 런타임에서 제어점 수를 업데이트하기 위해 시작합니다. 당시 우리는이 세트 방법을 개인에서 공개 범위로 이동할 수 있습니다.

질문을 이해하는 동안 예제에 여러 가지 개념적 문제가 있습니다.

  • 반환 값은 무엇입니까? getControlPointCount() 제어 지점의 수가 제한되지 않은 경우?
    • Maxint입니까?
    • 곡선의 현재 제어점 수가 있습니까 (따라서 이것이 가장 많은 포인트 수임이라고 말하는 논리를 깨뜨리는가?)
  • 실제로 Maxint 포인트로 곡선을 만들려고하면 어떻게됩니까? 당신은 결국 기억이 부족할 것입니다.

인터페이스 자체는 나에게 문제가있는 것 같습니다. 다른 표준 컬렉션 클래스와 마찬가지로 클래스는 포인트 수와 그 제한을 캡슐화해야합니다. AddControlPoint() 크기, 메모리 또는 기타 위반이 발생한 경우 오류를 반환해야합니다.

특정 답변에 관해서는 Kgiannakakis에 동의합니다. 회원 기능은 더 많은 유연성을 허용합니다.

프로그램 실행을 통해 모든 '안정적인'값에 대해 구성 + 상수 (기본값)를 사용하는 경향이 있습니다. 변경할 수없는 값에 대한 평범한 상수 (360도 -> 2 PI 라디안, 60 초 -> 1 분) 또는 변경 사항이 실행 코드를 깨뜨릴 수 있습니다 (불안정하게 만드는 알고리즘의 최소/최대 값).

당신은 몇 가지 다른 디자인 문제를 다루고 있습니다. 먼저 제어점 수가 클래스 또는 인스턴스 레벨 값인지 알아야합니다. 그런 다음 두 레벨 중 하나에서 상수인지 여부.

모든 곡선이 응용 프로그램에서 동일한 수의 제어점을 공유 해야하는 경우 클래스 레벨 (정적) 값입니다. 다른 곡선이 다른 수의 제어점을 가질 수 있다면 클래스 레벨 값이 아니라 인스턴스 레벨 1입니다.

이 경우, 곡선의 전체 수명 동안 제어 지점의 수가 일정하면 인스턴스 레벨 상수입니다. 변경할 수 있다면이 레벨에서도 일정하지 않습니다.

// Assuming that different curves can have different 
// number of control points, but that the value cannot 
// change dynamically for a curve.
class Curve
{
public:
   explicit Curve( int control_points )
      : control_points_( control_points )
   {}
   // ...
private:
   const int control_points_;
};

namespace constant
{
   const int spline_control_points = 8;
}
class Config
{
public:
   Config();
   void readFile( std::string const & file );

   // returns the configured value for SplineControlPoints or
   // constant::spline_control_points if the option does not 
   // appear in config.
   int getSplineControlPoints() const;
};

int main()
{
   Config config;
   config.readFile( "~/.configuration" ); // read config

   Curve c( config.getSplineControlPoints() );
}

적분 유형의 경우 다음을 사용하여 평소입니다.

class Curve
{
   public:
      enum 
      {
          CONTROL_POINT_COUNT = 8
      };
};

클래스 구현을 제외한 엔티티가 상수가 필요하지 않은 경우 *.cpp 파일로 상수를 선언합니다.

namespace
{
const int CONTROL_POINT_COUNT = 8;
}

일반적으로 모든 데이터는 개인이어야하며 게터와 세터를 통해 액세스해야합니다. 그렇지 않으면 캡슐화를 위반합니다. 즉, 기본 데이터를 노출시키는 경우 자신과 클래스를 해당 기본 데이터의 특정 표현으로 고정시킵니다.

이 특정한 경우에 나는 다음과 같은 일을했을 것입니다.

class Curve
{

   protected:

      int getControlPointCount() {return _controlPointCount;}
      int setControlPointCount(int c) { _controlPointCount = c; }

   private:

      static int _controlPointCount = 0;
};

일반적으로 상수는 내부 내부에서 정의되어서는 안됩니다. 당신이 선택한 예제에는 두 가지 독특한 기능이 있습니다. 첫째, 그것은 getter입니다. 둘째, 반환되는 유형은 int입니다. 그러나 상수를 정의하는 요점은 그것들을 두 번 이상 사용하고 편리한 방식으로 언급 할 수있는 것입니다. "ControlPointCount"와 달리 "8"을 입력하면 시간을 절약 할 수 있으며 유지 보수 비용이 발생하지 않을 수 있지만 항상 메서드 내부 상수를 정의하는 경우 일반적으로 사실이 아닙니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top