문제

나는 내부적으로 정기적으로 생성되고 파괴되는 일부 컨트롤러 개체가있는 C ++ 앱에서 작업하고 있습니다 (신규 사용). 이 컨트롤러는 다른 객체 (IT ControllerSupervisor라고 부르 자)에 스스로 등록하고 파괴 될 때 스스로 등록 해제해야합니다.

내가 지금 직면하고있는 문제는 응용 프로그램을 종료 할 때 발생합니다. 파괴 순서가 결정적이지 않기 때문에 컨트롤러 자체 (일부) 이전에 단일 컨트롤러 부수자 인스턴스가 파괴되고 등록 방법을 호출 할 때 발생합니다. 그들의 파괴자, 그들은 이미 파괴 된 대상을 만듭니다.

내가 지금까지 내가 생각해 낸 유일한 아이디어 (큰 추위가 많기 때문에 이것은 큰 의미가있을 수 있음)는 컨트롤러 서버 바이저를 스택의 글로벌 변수로 가지 않고 힙에 (즉, 새로운 사용)를 갖는 것입니다. 그러나이 경우 나는 그것을 삭제할 장소가 없습니다 (이것은 모두 타사 종류의 도서관에 있습니다).

가능한 옵션에 대한 힌트/제안에 감사 할 것입니다.

도움이 되었습니까?

해결책

관찰자 패턴을 사용할 수 있습니다. 컨트롤러는 감독자에게 파괴되고 있음을 전달합니다. 그리고 감독관은 파괴시 아이와 동일하게 의사 소통합니다.

보세요 http://en.wikipedia.org/wiki/observer_pattern

다른 팁

자동 변수 파괴 순서 (기능에서 사용하는 "정상적인"로컬 변수를 포함하는)는 생성의 역 순서입니다. 따라서 ControllerSupervisor를 상단에 놓으십시오.

글로벌의 파괴 순서는 또한 창조물의 반대에 있으며, 이는 차례로 정의 된 순서에 따라 다릅니다. 나중에 정의 된 객체는 나중에 생성됩니다. 그러나 다른 .CPP 파일 (변환 단위)에 정의 된 객체는 정의 된 순서로 생성되는 것을 보장하지 않습니다.

Mike가 권장하는 방식을 사용하는 것을 고려해야한다고 생각합니다.

  1. 창작물은 싱글 톤 패턴을 사용하여 (다른 번역 단위의 객체의 초기화 순서가 정의되지 않기 때문에) 기능 정적 감독자 객체에 대한 포인터를 반환함으로써 처음 사용합니다.
  2. 감독자는 일반적으로 파괴됩니다 (함수의 정적 파괴에 관한 규칙을 사용). 컨트롤러는 감독자의 정적 기능을 사용하는 데어 로이터입니다. 그 사람은 감독자가 이미 파괴되었는지 확인합니다 (포인터 확인 != 0). 그것이 있다면, 아무것도하지 않습니다. 그렇지 않으면 감독자에게 통보됩니다.

컨트롤러가 연결되어 있지 않은 감독자가있을 수 있기 때문에 (임시 만) 스마트 포인터를 사용하여 감독자를 자동으로 파괴 할 수 없습니다.

기본적으로 Alexandrescu의 Modern C ++ Design (CHAPER 6, Singletons)에는이 주제에 대한 전체 장이 있습니다. 그는 싱글 톤 사이에도 의존성을 관리 할 수있는 싱글 톤 클래스를 정의합니다.

전체 책은 너무 권장됩니다.

몇 가지 제안 :

  • ControllersUpervisor를 포인터를 반환하는 정적 메소드를 통해 액세스하는 싱글 톤 (또는 해당 목적을 위해 만든 싱글 톤 객체로 랩핑)으로 만들면 등록 된 객체의 DTORS가 정적 액세서 (응용 프로그램의 경우에 호출 할 수 있습니다. 셧다운과 컨트롤러 서적 범위가 파괴되면 NULL이 반환 될 것입니다. 그리고 해당 객체는이 경우 등록 방법을 호출하지 않을 수 있습니다.

  • 새로 사용하여 힙에 ControllersUpervisor를 만들고 다음과 같은 것을 사용하십시오. boost::shared_ptr<> 평생을 관리합니다. 나눠주십시오 shared_ptr<> 싱글 톤의 정적 액세서 방법에서.

GNU GCC/G ++는 매우 유용한 유형에 대한 비 휴대용 속성을 제공합니다. 이러한 속성 중 하나는입니다 init_priority 이는 글로벌 객체가 구성되는 순서와 결과적으로 파괴되는 역 순서를 정의합니다. 남자로부터 :

init_priority (우선 순위)

 In Standard C++, objects defined at namespace scope are guaranteed
 to be initialized in an order in strict accordance with that of
 their definitions _in a given translation unit_.  No guarantee is
 made for initializations across translation units.  However, GNU
 C++ allows users to control the order of initialization of objects
 defined at namespace scope with the init_priority attribute by
 specifying a relative PRIORITY, a constant integral expression
 currently bounded between 101 and 65535 inclusive.  Lower numbers
 indicate a higher priority.

 In the following example, `A' would normally be created before
 `B', but the `init_priority' attribute has reversed that order:

      Some_Class  A  __attribute__ ((init_priority (2000)));
      Some_Class  B  __attribute__ ((init_priority (543)));

 Note that the particular values of PRIORITY do not matter; only
 their relative ordering.

상황에 따라 다음을 수행 할 수 있습니다.

  1. 구린이 제안한대로 관찰자 패턴을 사용하십시오. 기본적으로 감독자는 컨트롤러에게 내려 가고 있음을 알려줍니다 ...
  2. 감독자에게 컨트롤러를 "소유"하도록하고 내려갈 때 파괴에 대한 책임을지게하십시오.
  3. 마지막으로 내려가는 사람이 실제 파괴를 할 수 있도록 컨트롤러를 shared_pointers에 보관하십시오.
  4. (스마트 포인터) 컨트롤러와 스택의 감독자를 모두 관리하여 파괴 순서를 결정할 수 있습니다.
  5. 기타 ...

실제 삭제를 위해 등록 된 컨트롤러 수를 Sentinel로 사용하는 것을 볼 수 있습니다.

그런 다음 삭제 된 전화는 요청에 불과하며 컨트롤러가 데드 레저 스티어가 될 때까지 기다려야합니다.

언급했듯이 이것은 관찰자 패턴의 하나의 사용입니다.

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
        inDeleteMode_ = true;
        if( controllers_.empty()){
            delete this;
        }
    }

    void deregister(Controller* controller) {
        controllers_.erase(
            remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
        if( inDeleteMode_ && controllers_.empty()){
            delete this;
        }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();

우아하지는 않지만 다음과 같은 일을 할 수 있습니다.

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

새로운 글로벌 :

ControllerCoordinator control;

컨트롤러를 건설 할 때마다 추가하십시오 control.supervisor.insert(controller). 당신이 하나를 파괴 할 때마다 추가하십시오 control.erase(controller). 당신은 피할 수 있습니다 control. Control.supervisor에 대한 글로벌 참조를 추가하여 접두사.

코디네이터의 감독관은 소멸자가 실행 된 후까지 파괴되지 않으므로 감독자가 컨트롤러를 오래 살리게 될 것을 보장합니다.

감독자가 컨트롤러를 파괴하는 것을 관리하는 것은 어떻습니까?

다른 곳에서 제안한 바와 같이, 감독자는 싱글 톤 (또는 유사하게 제어 된 대상, 즉 세션으로 범위를 지정)으로 만듭니다.

필요한 경우 싱글 톤 주변에 적절한 경비원 (theading 등)을 사용하십시오.

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
        controller_(NULL)
        {
            controller_ = Controller::create();
        }

    ~ControllerClient() {
        delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {        
        if (!theSupervisor ) {
            theSupervisor = Supervisor();
        }
        return *theSupervisor;
    } 

    void deregister(Controller& controller) {
        remove( controller );
        if( controllers_.empty() ) {
            theSupervisor = NULL;
            delete this;
        }       
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
        return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
        supervisor_->deregister(*this);
        supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
        supervisor_(&supervisor)
        {}
}

추악하지만 이것은 가장 간단한 방법 일 수 있습니다.

등록되지 않은 통화를 시도해보십시오. 많은 코드를 변경할 필요가 없으며 앱이 이미 종료되고 있으므로 큰 문제는 아닙니다. (또는 셧다운 순서에 대한 다른 비준이 있습니까?)

다른 사람들은 더 나은 디자인을 지적했지만 이것은 간단합니다. (그리고 못생긴)

이 경우 관찰자 패턴도 선호합니다.

이벤트를 사용하여 컨트롤러의 파괴를 알 수 있습니다

감독자의 소멸자에 WaitformultipleObjects를 추가하여 모든 컨트롤러가 파괴 될 때까지 기다릴 것입니다.

컨트롤러의 파괴자에서는 컨트롤러 종료 이벤트를 올릴 수 있습니다.

각 컨트롤러에 대한 글로벌 출구 이벤트 핸들을 유지해야합니다.

코트롤 감독자를 싱젤 턴으로 만드십시오. 제어 생성자가 건설 중에 감독자를 얻는지 확인하십시오 (후유증이 아님). 이를 통해 통제 감독관은 통제 전에 완전히 구성되어 있습니다. 통제 감독자 소멸자 전에 소멸자가 호출됩니다.

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };

C ++ 표준은 해당 변수가 모두 하나의 파일 ( "번역 장치")에 적합 할 때 초기화/파괴 순서를 지정합니다. 하나 이상의 파일에 걸쳐있는 것은 포트가 불가능 해집니다.

두 번째로 감독자가 각 컨트롤러를 파괴하도록 제안합니다. 이것은 감독관만이 누구에게도 자신을 파괴하라고 지시한다는 의미에서 안전합니다 (아무도 스스로 파괴하지 않음). 또한 교착 상태 가능성을 피해야 할 것입니다 (힌트 : 컨트롤러가 필요하지 않고 말한 후에는 컨트롤러가 스스로를 파괴 할 수 있는지 확인하십시오. 아무것 다른 감독자로부터).


프로그램이 끝나기 전에 (즉, 컨트롤러가 짧은 경우) 컨트롤러를 파괴해야 하더라도이 스레드를 안전하게 만들 수 있습니다.

첫째, 내가 나 자신을 파괴하기로 결정하고 나중에 마이크로 초에 감독관이 나를 파괴하기로 결정하고 나에게 그렇게 말하면 걱정하는 것은 인종 조건이 아닐 수도 있습니다.

둘째,이 인종 조건에 대해 우려하는 경우 모든 파괴 요청이 감독자를 통과하도록 요구함으로써이를 고칠 수 있습니다. 나는 감독자에게 나에게 말하거나 감독자에게 그 의도를 등록하라고 지시하고 싶다. 감독자를 포함하여 다른 사람이 나를 파괴하기를 원한다면 감독관을 통해 그렇게합니다.

이 질문에 대한 제목을 읽을 때 나는 즉시 "어떤 물건이 파괴 될 수 있는지 (파괴 되었는가?) 두 가지 물체가 그 방법을 채택하면 어떻게 될까요?"

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