문제

우선 너무 단순한 질문으로 오랜 시간 고민을 하게 된 점 사과드립니다.

저는 공간 채우기 곡선에서 매우 긴 1차원 인덱스 역할을 하는 클래스나 인덱스가 해당하는 데카르트 좌표를 나타내는 n-튜플을 구현하고 있습니다.

class curvePoint
{
public:
    friend class curveCalculate;

    //Construction and Destruction
    curvePoint(): point(NULL), dimensions(0) {}
    virtual ~curvePoint(){if(point!=NULL) delete[] point;}

    //Mutators
    void convertToIndex(){ if(isTuple()) calc(this); }
    void convertToTuple(){ if(isIndex()) calc(this); }
    void setTuple(quint16 *tuple, int size);
    void setIndex(quint16 *index, int size);
    void setAlgorithm(curveType alg){algorithm = alg;}

    //Inspectors
    bool isIndex(){return current==Index;}
    bool isTuple(){return current==Tuple;}
    size_t size(){return dimensions;}
    quint16 operator[](size_t index);

    enum curveType{HilbertCurve, ZCurve, GrayCodeCurve};
    enum status{Index, Tuple};

private:
    curveCalculate calc;
    curveType algorithm;
    quint16 *point;
    size_t dimensions;
    status current;
};

(다음이 가리키는 배열의 길이 가리키다 ~이다 치수)

어쨌든 Operator[] 구현에서 경계 검사를 수행하는 가장 좋은 방법이 무엇인지 궁금합니다.가능하다면 예외가 발생하는 것을 피하고 싶고 배열의 각 숫자에 대해 전체 값 범위를 사용할 수 있으므로 범위를 벗어난 오류가 발생한 경우 반환할 특수 값도 불가능합니다.

나는 클래스 정의에서 구현되었지만 다음과 같은 것을 생각하고 있었습니다.

quint16 curvePoint::operator[](size_t index)
{
    return point[ index % dimensions ];
}

이는 우리가 배열의 경계를 벗어나지 않도록 하며 잘 문서화되어 있다면 괜찮을 것이라고 생각합니다.그럼에도 불구하고 나는 이 특정 구현에 대해 회의적입니다.

이것이 다른 사람들에게 받아들여질 것 같나요?제약 조건을 만족시키면서 경계 검사를 수행할 수 있는 다른 방법이 있습니까?

편집하다:힐베르트 곡선 등과 같은 계산은 매우 지저분하고, stl 라이브러리에 대한 추가 인터페이스가 방해가 되는 것을 원하지 않을 만큼 충분히 지저분합니다.

또한 다차원 데이터베이스에 쿼리할 때마다 이러한 수천 개를 변환해야 하기 때문에 가능하다면 혼합 시 stl 함수 호출에 드는 추가 비용을 원하지 않습니다.

나는 오히려 주장의 아이디어를 좋아합니다.하지만 릴리스 빌드에서 중단이 발생했다는 사실을 올바르게 기억한다면 그렇지 않습니까?

나는 예외를 사용할 수 있다고 가정합니다. 그것은 모두가 응원하는 것 같지만 Qt 라이브러리를 사용하고 있으며 성능과 이식성 모두에 대한 예외를 피하고 나도 똑같이 하기를 바랐습니다.

도움이 되었습니까?

해결책

가장 쉬운 해결책은 C ++ 자체와 같이 수행하는 것입니다. 이것은 사용자가 경험할 놀라움의 양을 제한합니다.

C ++ 자체는 상당히 일관성이 있습니다. 두 내장 [] 포인터와 std::vector::operator[] 외부 배열 인덱스를 사용하는 경우 정의되지 않은 동작이 있습니다. 바운드 점검을 원한다면 명시 적으로 사용하십시오. std::vector::at

따라서 수업에 대해 똑같이하는 경우 노동 외의 행동을 "표준"으로 문서화 할 수 있습니다.

다른 팁

어쨌든 운영자의 구현에서 [] 나는 경계 점검을 달성하는 가장 좋은 방법이 무엇인지 궁금했습니다.가능한 경우 예외를 던지지 않으려 고하고, 배열의 각 숫자에 대해 전체 값 범위를 사용할 수 있으므로 경계를 벗어난 오류가 발생한 경우 리턴 할 특별한 값도 불가능합니다.

그러면 나머지 옵션은 다음과 같습니다.

  • 유연한 디자인. 당신이 한.의미가 있는 작업을 수행하도록 잘못된 입력을 "수정"합니다.이점:기능이 충돌하지 않습니다.불리:범위를 벗어난 요소에 액세스하는 무감각한 발신자는 거짓말하다 결과적으로.1층부터 10층까지의 10층짜리 건물을 상상해 보세요.

너: "3층에 누가 살아요?"

나: "메리".

너: "9층에는 누가 살아요?"

나: "조".

너: "1,203층에는 누가 살고 있나요?"

나:(기다리다...1,203% 10 = 3...) > "메리".

너: "와, Mary는 여기에서 멋진 경치를 감상해야 합니다. 거기.그럼 그 사람 아파트 두 채를 소유하고 있는 건가요?"

  • bool 출력 매개변수 성공 또는 실패를 나타냅니다.이 옵션은 일반적으로 별로 사용하기 어려운 코드로 끝납니다.많은 사용자는 반환 코드를 무시합니다.다른 반환 값으로 반환한 내용이 여전히 남아 있습니다.

  • 계약에 의한 디자인. 호출자가 경계 내에 있는지 확인합니다.(C++의 실용적인 접근 방식은 다음을 참조하세요. 예외인가, 버그인가?미로 사멕(Miro Samek) 또는 Pedro Guerreiro의 C++ 계약에 의한 설계에 대한 간단한 지원.)

  • 반환 System.Nullable<quint16>.이런, 잠깐만요. 이건 C#이 아닙니다.음, quint16에 대한 포인터를 반환할 수 있습니다.물론 여기에는 여기서 논의하지 않고 이 옵션을 사용할 수 없게 만드는 많은 의미가 있습니다.

내가 가장 좋아하는 선택은 다음과 같습니다.

  • 공개적으로 출시된 라이브러리의 공개 인터페이스의 경우:입력이 확인되고 예외가 발생합니다.귀하는 이 옵션을 배제했으므로 귀하에게 적합한 옵션이 아닙니다.그것은 아직도 나의 공개적으로 출시된 라이브러리의 인터페이스를 위한 선택입니다.
  • 내부 코드의 경우:계약에 의한 디자인.

저에게 있어이 솔루션은 버그를 찾기가 어려울 수 있기 때문에 용납 할 수 없습니다. 범위를 벗어난 예외를 던지는 것은 갈 길입니다.

필요한 것이 일종의 "원형"배열이라면 솔루션이 정상입니다. 그러나 나에게는 "안전한"논리 뒤에 인덱싱 연산자를 숨기는 것처럼 보이므로 제안 된 솔루션에 위배 될 것입니다.

인덱스 오버 플로우를 허용하고 싶지 않다면 예외를 확인하고 던질 수 있습니다.

quint16 curvePoint::operator[](size_t index)
{
    if( index >= dimensions)
    {
       throw std::overflow_error();
    }
    return point[ index ];
}

오버 헤드가 적 으면 디버그 시간 어시스트를 사용하여 예외를 피할 수 있습니다 (제공된 색인이 항상 유효하다고 가정).

quint16 curvePoint::operator[](size_t index)
{
    assert( index < dimensions);
    return point[ index ];
}

그러나 포인트 및 치수 멤버를 사용하는 대신 Point Storage에 std :: 벡터 <quint16>을 사용하는 것이 좋습니다. 이미 사용할 수있는 인덱스 기반 액세스가 있습니다.

quint16 curvePoint::operator[](size_t index)
{
    // points is declared as std::vector< quint16> points;
    return points[ index ];
}

실패하지 않는 연산자 []가 좋지 않지만 나중에 버그를 숨길 수 있습니다. 호출 함수가 불법 오프셋을 사용하고 버퍼의 시작에서 값을 찾아 유효한 값인 것처럼 진행됩니다.

경계 점검을 달성하는 가장 좋은 방법은 어설 션을 추가하는 것입니다.

quint16 curvePoint::operator[](size_t index)
{
    assert(index < dimensions);
    return point[index];
}

코드가 이미 부스트 라이브러리에 의존하는 경우 사용하고 싶을 수도 있습니다. BOOST_ASSERT 대신에.

내가 당신이라면 STL이 설정 한 예제를 따를 것입니다.

이 경우 std::vector 두 가지 방법을 제공합니다. at 확인 된 경계입니다 operator[] 그렇지 않습니다. 이를 통해 클라이언트가 사용하기로 결정할 수 있습니다. 나는 확실히 사용하지 않을 것이다 % size(), 이것은 버그를 숨 깁니다. 그러나 Bounds Checking은 큰 컬렉션을 반복 할 때 많은 오버 헤드가 추가되므로 선택 사항이어야합니다. 나는 다른 포스터에 동의하지만, 이로 인해 디버그 빌드에서 성능이 적용되는 것만으로도 좋은 생각이라는 것이 매우 좋은 아이디어입니다.

또한 Const 버전이 아닌 참조 및 Const 공급을 고려해야합니다. 다음은 기능 선언입니다 std::vector:

reference at(size_type _Pos);
const_reference at(size_type _Pos) const;

reference operator[](size_type _Pos);
const_reference operator[](size_type _Pos) const;

API를 지정하는 방법을 잘 모르면 좋은 경험 법칙으로 다른 사람들이 유사한 API를 지정하는 방법에 대한 예를 찾습니다. 또한 API를 사용하면 판단하거나 평가하려고합니다. 내가 좋아하는 비트를 찾아서 싫어합니다.

Daniel Daranas의 게시물에있는 C# 기능에 대한 의견 덕분에 가능한 솔루션을 알아낼 수있었습니다. 내 질문에서 언급했듯이 QT 라이브러리를 사용하고 있습니다. QVariant를 사용할 수 있습니다. QVariant는 수신 된 기능으로 확인할 수있는 유효하지 않은 상태로 설정할 수 있습니다. 따라서 코드는 다음과 같습니다.

QVariant curvePoint::operator[](size_t index){
    QVariant temp;
    if(index > dimensions){
        temp = QVariant(QVariant::Invalid);
    }
    else{
        temp = QVariant(point[index]);
    }

    return temp;
}

물론 이것은 기능에 약간의 오버 헤드를 삽입 할 가능성이 있으므로 또 다른 가능성은 쌍 템플릿을 사용하는 것입니다.

std::pair<quint16, bool> curvePoint::operator[](size_t index){
    std::pair<quint16, bool> temp;
    if(index > dimensions){
        temp.second = false;
    }
    else{
        temp.second = true;
        temp.first = point[index];
    }
    return temp;
}

또는 정확히 같은 기능을 갖고 STL을 연결할 필요가 없도록 QPair를 사용할 수 있습니다.

아마도 [] 연산자 (또는 적어도 어설 싱)에 "경계 외"예외를 추가 할 수 있습니다.

이것은 특히 디버깅 할 때 문제를 해결해야합니다.

내가 무언가를 오해하지 않는 한,

return point[ index % dimensions ];

전혀 확인하는 바운드가 아닙니다. 그것은 완전히 다른 줄에서 실제 가치를 반환하여 버그를 감지하기가 훨씬 어려워집니다.

나는 둘 중 하나입니다 :

  1. 예외 나 주장을 던지십시오 (그렇게하고 싶지 않다고했지만)
  2. 배열을 지나서 "자연스러운"방식으로 단순히 피해를 입는다 (즉, 내부 점검을 건너 뛰십시오). %에 비해 장점은 "이상한"값 및/또는 액세스 위반을 얻을 가능성이 높다는 것입니다.

결국, 발신자는 사전 조건을 위반하고 있으며 원하는대로 할 수 있습니다. 그러나 나는 이것이 가장 합리적인 옵션이라고 생각합니다.

또한 CĂtĂ린이 내장 STL 컬렉션을 통합하는 것에 대해 말한 내용을 고려하십시오.

타원 모양의 지점에 대한 액세스를 제공하는 경우 솔루션이 좋을 것입니다. 그러나 임의의 기하학적 기능에 사용하면 매우 불쾌한 버그로 이어질 것입니다.

모듈로 연산자는 놀랍게도 어레이 지수에 잘 작동합니다. 또한 부정적인 지수를 구현합니다 (즉. point[-3] = point[dimensions - 3]). 이것은 작업하기 쉽기 때문에 잘 문서화되어있는 한 모듈로 운영자를 개인적으로 추천합니다.

또 다른 옵션은 발신자가 바운드 외 정책을 선택하도록하는 것입니다. 고려하다:

template <class OutOfBoundsPolicy>
quint16 curvePoint::operator[](size_t index)
{
    index = OutOfBoundsPolicy(index, dimensions);
    return point[index];
}

그런 다음 발신자가 선택할 수있는 몇 가지 정책을 정의 할 수 있습니다. 예를 들어:

struct NoBoundsCheck {
    size_t operator()(size_t index, size_t /* max */) {
        return index;
    }
};

struct WrapAroundIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        return index % max;
    }
};

struct AssertIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        assert(index < max);
        return index % max;
    }
};

struct ThrowIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        if (index >= max) throw std::domain_error;
        return index;
    }
};

struct ClampIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        if (index >= max) index = max - 1;
        return index;
    }
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top