Vector3는 Vector2에서 상속해야합니까?


18

몇 가지 클래스 Vector2(X & Y)와 Vector3(X, Y & Z)를 만들고 있지만 Vector3상속 Vector2여부 또는 멤버 변수 m_xm_y다시 구현할지 여부를 모릅니다 . 각 측면의 장단점은 무엇입니까 (상속 대 재정의).

편집 : C ++ (VS2010)을 사용하고 있습니다.


1
왜 n 차원 벡터에 대한 일반 벡터 클래스를 작성하고 (필요한 경우) vector2 및 vector3 클래스를 상속하지 않습니까? 일반 클래스에 템플릿을 사용하고 정수 벡터와 부동 벡터에 대한 버전도 상속 할 수 있습니다. 편집 : 최적화 된 수학 라이브러리를 사용하지 않는 이유는 무엇입니까?
danijar

5
상상력 "Vector3가 Vector2이다"없이 스트레칭함으로써, 그들은 부모 VectorN에서 모두 상속은 비록 수

1
예, 그러나 아마도 가상 테이블이 필요할 것이며 런타임 및 메모리 비용이 중요한 경우 중 하나입니다. 이상적으로 a Vector3floats메모리와 관련하여 3에 불과 합니다. 그것이 불가능하다는 말은 아니지만, 나는 그것이 생산 엔진에서 그것을 본 적이 없다는 것입니다.
Laurent Couvidou

2
예, 그렇게 생각합니다. 당신이 다른 것을 필요로하지 않는 한 floats. YAGNI, KISS 등 모든 것을 알고 있습니다. Vector2, Vector3그리고 Vector4어떤 상속과 floats만 정말 게임 엔진에서 사실상의 표준이다.
Laurent Couvidou

1
나는 당신이 의미하기를 바랍니다 typedef float real;.).
Mark Ingram

답변:


47

아닙니다. 상속에서 사용하는 유일한 것은 xy구성 요소입니다. Vector2클래스 에서 사용되는 메소드는 클래스 에서 유용하지 않으며 Vector3, 다른 인수를 사용하고 다른 수의 멤버 변수에 대해 조작을 수행 할 수 있습니다.


+1, 팝업에 더주의를 기울여야하므로 중복 된 내용은 쓰지 않습니다.
Matsemann

8
고전 상속 과용 . A는 Vector3IS-NOT-A Vector2(그래서하지 상속 함)하지만,이 AppleIS-A Fruit(이 상속 수 있으므로). 당신이 당신의 마음을 충분히 트위스트 경우는 Vector3-A가 Vector2그 안에하지만, 성능 손실과 어려움 코딩 방법은 당신을 위해 완전히 별도의 클래스를 작성합니다 Vector3Vector2.
bobobobo

그러나 2d 벡터와 3d 벡터를 상속하기 위해 n 차원 벡터 클래스를 작성할 수 있습니다.
danijar

8

C ++로 할 수있는 호기심 많은 일이 있습니다 (언어를 지정하지 않았 으며이 답변은 대체 방법을 보는 것이 좋지만 대부분의 경우 유용하다고 생각하지는 않지만 대체로 보는 것이 좋기 때문입니다.)

템플릿을 사용하면 다음과 같은 작업을 수행 할 수 있습니다.

template <class T, class S, int U>
class VectorN
{
    protected:
        int _vec[U];
    public:
        S& operator+=(const S c)
        {
            for(int i = 0; i < U; i++)
            {
                _vec[i] += c.at(i);
            }
            return (S&)*this;
        }
        int at(int n) const
        {
            return _vec[n];
        }
};

template <class T>
class Vec2 : public VectorN<T,Vec2<T>,2>
{
    public:
        T& x;
        T& y;
        Vec2(T a, T b) : x(this->_vec[0]), y(this->_vec[1])
        {
            this->_vec[0] = a;
            this->_vec[1] = b;
        }
};

template <class T>
class Vec3 : public VectorN<T,Vec3<T>,3>
{
    public:
        T& x;
        T& y;
        T& z;
        Vec3(T a, T b, T c) : x(this->_vec[0]), y(this->_vec[1]), z(this->_vec[2])
        {
            this->_vec[0] = a;
            this->_vec[1] = b;
            this->_vec[2] = c;
        }
};

그리고 이것은 다음과 같이 사용될 수 있습니다 :

int main(int argc, char* argv[])
{

    Vec2<int> v1(5,0);
    Vec2<int> v2(10,1);

    std::cout<<((v1+=v2)+=v2).x;
    return 0;
}

내가 말했듯이, 이것이 유용하지 않다고 생각하고 도트 / 정규화 / 다른 것들을 구현하려고 시도하고 많은 수의 벡터와 일반화하려고하면 인생이 복잡해질 것입니다.


모든 꺾쇠 괄호는 것 - 예, 모든 genericness 좋은이 만 대부분의 경우에만 3 개 부동 소수점 구성 요소와 표준 3 벡터를 필요로하는 시간 보인다 Vector3f v상당히 더 bloatyVector3<float> v
bobobobo

@bobobobo 예, 동의합니다. 내 벡터 클래스는 일반적으로 부모가없는 vec2 및 vec3이지만 여전히 템플릿으로 만듭니다. Vector3 <float>를 작성하는 것이 귀찮다면 언제든지 typedef를 작성할 수 있습니다
.

.. 그리고 이제는 C 프로그래머의 주장 .. "하지만 템플릿을 사용하기 위해 컴파일 시간을 늘리는 것은 어떤가요 ?? 이 경우 정말 가치가 있습니까?
bobobobo

@bobobobo 나는 컴파일 시간에 아무런 문제가 없었습니다 : P, 그러나 컴파일 시간이 문제가되는 프로젝트에서는 결코 일하지 않았습니다. 컴파일 시간은 정수가 필요할 때 부동 소수점을 사용하지 않는 것이 정당하다고 주장 할 수 있습니다.
Luke B.

@bobobobo 명시 적으로 인스턴스화하고 인라인 파일을 헤더에 포함하지 않으면 컴파일 시간이 달라지지 않습니다. 또한 템플릿 앵글 브래킷 팽창 이 한 번만 가능 typedef합니다.
Samaursa

7

할 때에 관계없이 속도, 첫 번째 질문은 당신 자신에게 물어 봐야 어떤 당신이 다형을 사용하는 거라면 상속입니다. 좀 더 구체적으로 말하자면, Vector3마치 마치 마치 Vector2(Vector3 "is-a"Vector2라고 명시 적으로 말하는 것처럼) 자신을 사용하여 자신을 볼 수있는 상황이 있습니까 ?

그렇지 않으면 상속을 사용하지 않아야합니다. 코드를 공유하기 위해 상속을 사용해서는 안됩니다. 그것이 컴포넌트와 외부 함수를위한 것입니다. 어쨌든 그들 사이에 코드를 공유하지 않을 것입니다.

즉, 당신은 쉬운 방법을 원할 수 있습니다 변환 Vector3 에의 Vector2의를, 그 경우 당신은 암시 적으로 자릅니다 운영자 과부하 쓸 수 Vector3A를을 Vector2. 그러나 당신은 상속해서는 안됩니다.


고마워, 나는 그것이 문제를 강조했다고 생각한다. 나는 이것을 "코드 공유"관점에서 본 것이다 (즉, X & Y 값을 "재 입력"할 필요가 없다).
Mark Ingram

+1 큰 대답, 다른 크기의 벡터 사이에 다형성 사용이 없습니다.
Luke B.

이것은 내 대답에 추가 할 가장 큰 것입니다-+1. ( 다형성을 원한다고 상상할 있는 이상한 상황이 있지만 -거리 확인, 경로 지정 등과 같은 것들이 정식으로 2d에서 수행되기를 원하지만 객체에 대해 3D 좌표를 제공 해야하는 2.5d 'heightmap'게임)
Steven Stadnicki

@LukeB. OP의 경우 Vector2에는 기본에서 상속 받지만 상속받을 이유가 없다고 동의합니다 Vector<N>. 완벽하게 이해됩니다. 또한 상속이 자동으로 다형성 동작을 의미하는 이유는 무엇입니까? C ++의 가장 좋은 점 중 하나는 비용 상속이 전혀 없다는 것입니다. 기본 클래스 에 가상 메소드 (가상 소멸자를 포함) 를 추가 필요가 없습니다 Vector<N>.
Samaursa

5

아니요, 모든 메서드를 재정의해야하므로 실제로 상속 할 필요가 없습니다.

무언가라면, 벡터 인터페이스를 모두 구현할 수 있습니다. 그러나 Vector2와 Vector3 사이에 추가 / 하위 / 도트 / dst를 추가하지 않으려는 경우 원하지 않는 부작용이 있습니다. 그리고 다른 매개 변수 등을 갖는 것은 번거로울 것입니다.
따라서이 경우 상속 / 인터페이스의 장점을 실제로 볼 수 없습니다.

예를 들어 Libgdx 프레임 워크가 있는데, Vector2Vector3 는 동일한 유형의 메서드를 제외하고는 서로 관련이 없습니다.


2

SIMD를 사용하려는 경우 어레이 가장 좋습니다. 연산자 오버로딩을 계속 사용하려면 인터페이스 / 믹스 인을 사용하여 기본 배열에 액세스하는 것을 고려할 수 있습니다 Add.

내가 X/ Y/을 제공하지 않은 방법에 주목하십시오 Z. 각 VectorX클래스는 다른 사람들이 지정한 것과 같은 이유로이 클래스에서 직접 상속받습니다. 아직도, 나는 배열이 야생에서 벡터로 여러 번 사용되는 것을 보았습니다.

#include <xmmintrin.h>

class Vector
{
public:
    Vector(void)
    {
        Values = AllocArray();
    }

    virtual ~Vector(void) 
    { 
        _aligned_free(Values);
    }

    // Gets a pointer to the array that contains the vector.
    float* GetVector()
    {
        return Values;
    }

    // Gets the number of dimensions contained by the vector.
    virtual char GetDimensions() = 0;

    // An example of how the Vector2 Add would look.
    Vector2 operator+ (const Vector2& other)
    {
        return Vector2(Add(other.Values));
    }

protected:
    Vector(float* values)
    {
        // Assume it was created correctly.
        Values = values;
    }

    // The array of values in the vector.
    float* Values;

    // Adds another vector to this one (this + other)
    float* Add(float* other)
    {
        float* r = AllocArray();

#if SSE
        __m128 pv1 = _mm_load_ps(Values);
        __m128 pv2 = _mm_load_ps(other);
        __m128 pvr = _mm_load_ps(r);

        pvr = _mm_add_ps(pv1, pv2);
        _mm_store_ps(r, pvr);

#else
        char dims = GetDimensions();
        for(char i = 0; i < dims; i++)
            r[i] = Values[i] + other[i];
#endif

        return r;
    }

private:

    float* AllocArray()
    {
        // SSE float arrays need to be 16-byte aligned.
        return (float*) _aligned_malloc(GetDimensions() * sizeof(float), 16);
    }
};

면책 조항 : C ++은 빨 랐을 수도 있습니다. 사용 한 지 오래되었습니다.


잠깐만 , 내가_aligned_malloc 사용한 버그 가 실제로 버그가 아님 을 의미 합니까?
bobobobo

값을 __m128레지스터 로 가져 오기 위해 포인터 캐스트를 사용 _mm_loadu_ps하지 말고 대신 사용해야 합니다. 좋은 샘플 클래스는 여기에 "vectorclass.zip"아래
bobobobo

@bobobobo 나는 편집에 최선을 다할 것입니다-면책 ​​조항에 특별한주의를 기울이십시오;).
Jonathan Dickinson

@bobobobo _mm_loadu_ps는 그 구조체로 당신을 위해 일해야합니다 ( _mm_load_ps그렇지 않을 곳 ). 나는 또한 당신의 제안을 추가했습니다-당신이 내가 틀린 나무를 짖고 있다고 생각하면 질문을 자유롭게 편집하십시오 (C [++]를 사용한 지 오래되었습니다).
Jonathan Dickinson

1

Vec3이 Vec2에서 상속 받거나 두 가지 모두 단일 Vector 클래스에서 상속받는 데 대한 또 다른 심각한 단점 : 코드가 많이 수행됩니다.벡터에 대한 작업, 종종 시간이 중요한 상황에서 이러한 작업을 최대한 빨리 수행하는 것이 최선의 이익입니다. 그렇지 않은 다른 많은 개체보다 훨씬 빠릅니다. 매우 보편적이거나 저수준입니다. 좋은 컴파일러는 상속 오버 헤드를 평탄화하기 위해 최선을 다하지만, 여전히 원하는 컴파일러보다 더 많은 컴파일러에 의존하고 있습니다. 대신 가능한 한 적은 오버 헤드로 구조체로 빌드하고 가능한 경우 (실제로 도움이 될 수없는 operator +와 같은 것을 제외하고) 함수를 사용하는 대부분의 함수를 시도하고 만들 수 있습니다. 구조체. 조기 최적화는 일반적으로 권장되며 탁월한 이유가 있습니다.


1
-1 때문에 : class와 struct는 C ++에서 액세스 권한 부여와 관련이 있고 OP는 언어를 지정하지 않았으며, 가상이 아닌 멤버 함수는 멤버가 아닌 함수 및 가상 멤버 함수 (잠재적으로 나타날 수있는 함수)와 동일한 성능 의미를 갖습니다. 관심있는 문제)는 단순히 상속을 사용하는 것이 아니라 문제를 만드는 경우에만 존재합니다.

2
@JoshPetrie 모든 전선에서 유효합니다. 나는 C / C ++의 '기본'경향이 있으므로 그 렌즈를 통해 질문을 보았습니다. 내가 않는 합법적 인 성능 (뿐만 아니라 개념)을 마음, 상속 경로를하지 않을 이유가있다 생각하지만, 나는 훨씬 더 구체적인 세부 사항에 수 있었다. 나는 이것을 다시 시도하고 더 나은 회계를 줄 수 있는지 알아볼 것입니다.
Steven Stadnicki
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.