문제

지난 4 년 동안 C#에서 보냈으므로 C ++의 현재 모범 사례와 일반적인 설계 패턴에 관심이 있습니다. 다음 부분 예를 고려하십시오.

class World
{
public:
    void Add(Object *object);
    void Remove(Object *object);
    void Update();
}

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this;
        }
    }
}

여기에는 일련의 객체를 관리하고 정기적으로 업데이트 할 책임이있는 세상이 있습니다. 화재는 다양한 상황에서 세계에 추가 될 수 있지만 일반적으로 이미 세계에있는 다른 물체에 의해 추가 될 수있는 대상입니다. 화재는 그것이 타 버렸을 때를 아는 유일한 대상이므로 현재 자체적으로 삭제하고 있습니다. 화재를 일으킨 대상은 더 이상 존재하지 않거나 관련이 없을 것입니다.

이것은 합리적인 일입니까, 아니면 이러한 물체를 정리하는 데 도움이되는 더 나은 디자인이 있습니까?

도움이 되었습니까?

해결책

이것의 문제는 실제로 대상과 세계적 수업 사이에 암시 적 커플 링을 만들고 있다는 것입니다.

세계적 수준 이외의 업데이트 ()를 호출하려고하면 어떻게됩니까? 나는 물체가 삭제되는 것으로 끝날 수 있으며, 이유를 모른다. 책임이 심하게 혼합 된 것 같습니다. 이것은 당신 이이 코드를 썼을 때 생각하지 않은 새로운 상황에서 화재 클래스를 사용하는 순간 문제를 일으킬 것입니다. 물체를 둘 이상에서 삭제 해야하는 경우 어떻게됩니까? 아마도 그것은 세계, 현재지도 및 플레이어의 인벤토리에서 모두 제거해야합니까? 업데이트 기능은 세계에서이를 제거한 다음 객체를 삭제하고 다음에 맵이나 인벤토리가 객체에 액세스하려고 할 때 나쁜 일이 발생합니다.

일반적으로 업데이트중인 객체를 삭제하는 것은 업데이트 () 함수가 매우 직관적이지 않다고 말합니다. 또한 물체가 삭제하는 것은 직관적이지 않다고 말합니다. 물체는 불타고 있다는 사건을 발사 할 수있는 방법이 더 있어야하며, 관심있는 사람은 이제 그에 따라 행동 할 수 있습니다. 예를 들어 세상에서 제거하여. 그것을 삭제하려면 소유권 측면에서 생각하십시오.

누가 물체를 소유합니까? 세계? 그것은 세상만으로도 대상이 언제 죽는지를 결정한다는 것을 의미합니다. 대상에 대한 세계의 언급이 다른 언급을 오래 지속하는 한 괜찮습니다. 당신은 대상이 자신을 소유하고 있다고 생각합니까? 그게 무슨 뜻입니까? 객체가 더 이상 존재하지 않을 때 객체를 삭제해야합니까? 말이되지 않습니다.

그러나 명확하게 정의 된 단일 소유자가없는 경우 공유 소유권을 구현합니다 (예 : 스마트 포인터 구현 참조 계산). boost::shared_ptr

그러나 객체 자체에 멤버 함수가 있는데 하나 특정 목록은 존재하는지 여부와 다른 목록에도 존재하는지 여부에 관계없이, 어떤 참조에 상관없이 객체 자체를 삭제하는 것이 나쁜 생각입니다.

다른 팁

당신은 만들었습니다 Update 파생 클래스가의 구현을 무시할 수 있음을 제안하는 가상 함수 Update. 이것은 두 가지 큰 위험을 초래합니다.

1.) 재정의 구현은 World.Remove, 그러나 잊을 수 있습니다 delete this. 메모리가 유출됩니다.

2.) 재정의 구현은 기본 클래스를 호출합니다 Update, 이는 a delete this, 그러나 더 많은 작업이 진행되지만이 점이 잘못된이 점이로 진행됩니다.

이 예를 고려하십시오 :

class WizardsFire: public Fire
{
public:
    virtual void Update()
    {
        Fire::Update();  // May delete the this-object!
        RegenerateMana(this.ManaCost); // this is now invaild!  GPF!
    }
}

C ++에서 자신을 삭제하는 객체에는 실제로 잘못된 것이 없으며, 대부분의 참조 계산은 비슷한 것을 사용합니다. 그러나 약간의 난해한 기술로 여겨지 며 소유권 문제에 대해 매우주의를 기울여야합니다.

삭제가 실패하고 개체가있는 경우 프로그램에 충돌 할 수 있습니다. ~ 아니다 새로 할당 및 건설. 이 구성은 스택에서 또는 정적으로 클래스를 인스턴스화하지 못하게합니다. 귀하의 경우, 귀하는 일종의 할당 체계를 사용하는 것으로 보입니다. 당신이 그것을 고수한다면, 이것은 안전 할 수 있습니다. 그래도 나는 그것을하지 않을 것입니다.

나는 더 엄격한 소유권 모델을 선호합니다. 세상은 그 안에 물건을 소유하고 청소할 책임이 있어야합니다. World를 제거하거나 업데이트 (또는 다른 함수)가 객체를 삭제해야 함을 나타내는 값을 반환하도록하십시오.

또한 이런 유형의 패턴에서 당신은 매달려있는 포인터로 끝나지 않도록 당신의 객체를 계산하고 싶을 것이라고 추측하고 있습니다.

그것은 확실히 생성 된 개체에 대해 작동합니다 new 그리고 발신자의 경우 Update 그 행동에 대해 올바르게 알 수 있습니다. 그러나 나는 그것을 피할 것입니다. 귀하의 경우, 소유권은 분명히 세상에 있으므로 세상을 삭제하게 할 것입니다. 객체는 스스로를 생성하지 않으며 자체적으로 삭제해서는 안된다고 생각합니다. 객체에서 함수를 "업데이트"라고 부르면 매우 놀라운 일이지만 갑자기 그 객체가 세상을 벗어나지 않고 더 이상 존재하지 않는다 (목록에서 제거하는 것 외에는 다른 프레임에서! 코드 호출 객체에 대한 업데이트는 알 수 없습니다).

이것에 대한 몇 가지 아이디어

  1. 세계에 객체 참조 목록을 추가하십시오. 해당 목록의 각 개체는 제거를 위해 계류 중입니다. 이것은 일반적인 기술이며 닫힌 Toplevel Windows의 WXWidget에서 사용되지만 여전히 메시지를받을 수 있습니다. 유휴 시간에는 모든 메시지가 처리되면 보류중인 목록이 처리되고 객체가 삭제됩니다. 나는 QT가 비슷한 기술을 따른다고 생각합니다.
  2. 물체가 삭제되기를 원한다고 세상에 알리십시오. 세상은 제대로 정보를 받고해야 할 일을 처리 할 것입니다. A와 같은 것 deleteObject(Object&) 아마도.
  3. 을 추가하다 shouldBeDeleted 객체가 소유자가 삭제하려는 경우 TRUE를 반환하는 각 객체에 기능합니다.

나는 옵션 3을 선호합니다. 세상은 업데이트를 호출 할 것입니다. 그 후, 객체를 삭제 해야하는지 여부와 그렇게 할 수 있는지 또는 원하는 경우 해당 객체를 보류중인 제거 목록에 수동으로 추가하여 해당 사실을 기억합니다.

객체의 기능과 데이터에 언제 접근 할 수없는시기를 확신 할 수 없을 때 그것은 엉덩이의 고통입니다. 예를 들어, WXWIDGETS에는 두 가지 모드로 작동 할 수있는 wxthread 클래스가 있습니다. 이러한 모드 중 하나 ( "분리 가능"이라고 함)는 주 함수가 반환되면 (및 스레드 리소스가 릴리스되어야 함) 스레드의 소유자를 기다리는 대신 자체 (WXTHREAD 객체가 차지하는 메모리를 해제)를 삭제한다는 것입니다. 대기를 호출하거나 결합 함수를 호출합니다. 그러나 이것은 심한 두통을 유발합니다. 어떤 상황에서도 종료 될 수 있었기 때문에 어떤 기능도 호출 할 수 없으며, 새로운 기능을 만들 수 없기 때문에 기능을 호출 할 수 없습니다. 꽤 많은 사람들은 그 행동을 매우 싫어한다고 말했습니다.

기준 계수 된 물체의 자체 삭제가 냄새가납니다. 비교하자 :

// bitmap owns the data. Bitmap keeps a pointer to BitmapData, which
// is shared by multiple Bitmap instances. 
class Bitmap {
    ~Bitmap() {
        if(bmp->dec_refcount() == 0) {
            // count drops to zero => remove 
            // ref-counted object. 
            delete bmp;
        }
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount();
    int inc_refcount();

};

자가 삭제 refcounted 객체와 비교하십시오.

class Bitmap {
    ~Bitmap() {
        bmp->dec_refcount();
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount() {
        int newCount = --count;
        if(newCount == 0) {
            delete this;
        }
        return newCount;
    }
    int inc_refcount();
};

나는 첫 번째가 훨씬 더 좋다고 생각하며 잘 설계된 참조 수는 객체가 ~ 아니다 커플 링이 증가하기 때문에 "삭제"를 수행하십시오. 참조 계수 데이터를 사용하는 클래스는 데이터가 참조 계수를 줄이는 부작용으로 자체를 삭제한다는 것을 알고 기억해야합니다. "BMP"가 ~ BitMap의 소멸자에서 매달려있는 포인터가 될 수있는 방법에 유의하십시오. 아마도 "삭제"를하지 않는 것이 여기에서 훨씬 더 좋습니다.

비슷한 질문에 대한 답변 "이것을 삭제하는 것은 무엇입니까?"

그것은 상당히 일반적인 참조 계산 구현이며 이전에 성공적으로 사용했습니다.

그러나 나는 또한 그것이 충돌하는 것을 보았다. 충돌 상황을 기억할 수 있기를 바랍니다. @abelenky 내가 충돌하는 것을 본 곳입니다.

서브 클래스를 더 추가로했을 수도 있습니다 Fire, 그러나 가상 소멸자를 만들지 못합니다 (아래 참조). 가상 소멸자가 없을 때 Update() 기능이 호출됩니다 ~Fire() 적절한 소멸자 대신 ~Flame().

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this; //Make sure this is a virtual destructor.
        }
    }
};

class Flame: public Fire
{
private: 
    int somevariable;
};

다른 사람들은 "이것을 삭제"하는 문제를 언급했습니다. 요컨대, "세계"는 화재 창출을 관리하기 때문에 삭제를 관리해야합니다.

또 다른 문제는 세상이 여전히 불타고있는 것으로 끝나는 것입니다. 이것이 화재를 파괴 할 수있는 유일한 메커니즘이라면, 고아 나 쓰레기 화재로 끝날 수 있습니다.

원하는 의미의 경우 "Fire"클래스 (또는 해당되는 경우 개체)에 "활성"또는 "살아있는"플래그가 있습니다. 그런 다음 세상은 때때로 객체의 인벤토리를 확인하고 더 이상 활성화되지 않은 물건을 제거 할 것입니다.

--

한 가지 더 한 가지 메모는 서면으로 코드가 사격을 가졌다는 것입니다. 왜냐하면 공개 억제보다 훨씬 덜 일반적이지만 기본값이기 때문입니다. 당신은 아마도 상속 재산을 명시 적으로 만들어야 할 것입니다. 그리고 나는 당신이 정말로 당신이 정말로 공개적 인 유전을 원한다고 생각합니다.

매우 간단한 방식으로 필요하거나 사용하지 않는 한 "삭제"를하는 것은 일반적으로 좋은 생각이 아닙니다. 귀하의 경우 세상이 제거 될 때 객체를 삭제할 수있는 것처럼 보입니다 (다른 종속성이없는 한). 객체 외부에서 소유권을 유지하면 객체가 더 잘 캡슐화되고 안정적이 될 수 있습니다. 객체는 스스로 삭제하기로 선택했기 때문에 어느 시점에서 객체가 유효하지 않습니다.

나는 이것이 본질적으로 객체 자체를 삭제하는 데 잘못된 것이 없지만, 또 다른 가능한 방법은 :: 제거의 일부로 세상이 물체를 삭제할 책임이있는 것입니다 (제거 된 모든 객체도 삭제되었다고 가정합니다.)

삭제 이것은 매우 위험합니다. 그것에 "잘못된"것은 없습니다. 합법적입니다. 그러나 사용할 때 잘못 될 수있는 것들이 너무 많아서 드물게 사용해야합니다.

누군가가 열린 포인터 나 객체에 대한 참조를 가지고있는 경우를 고려한 다음 자체가 삭제됩니다.

대부분의 경우, 객체 수명은 그것을 생성하는 것과 동일한 객체에 의해 관리되어야한다고 생각합니다. 그것은 단지 파괴가 어디에서 발생하는지 쉽게 알 수있게합니다.

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