세부 사항은 C ++ 멤버 변수 및 생성자 초기화 순서의 성공 또는 실패를 결정합니다.

테스트 작성

class Runnable {
protected:
    Runnable () : thread(NULL) {
	pthread_create(&thread, NULL, Runnable::Run, this);
    }
    ~Runnable () {
        if (NULL != thread) {
            // Destroy thread.
            pthread_join(thread, NULL);
        }
    }   
protected:
    // Override this interface which extended runnable.
    virtual void Run (void) {
    }
private:
    static void* Run (void* args) {
        Runnable* runnable = static_cast<Runnable*>(args);
        runnable->Run();
        return NULL;
    }     

private:
    pthread_t     thread;
};
class Worker : public Runnable {
public:
    Worker () : quit(false) {
    }
    ~Worker () {
        // Set quit flag.
        quit = true;
    }

protected:
    // Override thread run function.
    void Run (void) override { 
        while (!quit) {
        // TODO somethine
        }
    }

private:
    volatile bool quit;                 // Worker thread quit flag.
    // OTHER member variables
};

       위 프로그램에서 버그를 찾아보세요.

사례 연구

       위에 작성된 테스트 질문의 코드는 특정 확률로 충돌하거나 응답하지 않습니다. 그 이유는 C ++ 멤버 변수 및 생성자의 초기화 순서와 관련이 있습니다. C ++에서 변수 나 개체를 만드는 것은 다음 두 단계로 볼 수 있습니다.

  1. 유형과 동일한 크기의 메모리를 적용합니다. 기본적으로 malloc (sizeof (T));
  2. 요청 된 메모리의 값이 임의의 값이기 때문에이 유형의 생성자를 호출하여 개체의 초기화 작업을 구현합니다.

이것은 그 자체로 C ++ 컴파일러를 하나로 결합 할 수있는 2 단계 문제이며 프로그래머는 프로그래머의 관리하에이를 한 단계로 단순화하여 버그를 활용할 수 있습니다. 상속 후 클래스의 생성자 인 경우 C ++는 먼저 부모 클래스의 생성자를 호출 한 다음 하위 클래스의 생성자를 호출합니다.

이것에 대해 말하자면, 독자들이 위 코드의 버그를 알고 있는지 궁금합니다. 문제는 다음 코드에 있습니다.

Worker () : quit(false) {
}

Worker는 Runnable의 하위 클래스입니다. 즉, 프로그램이 Runnable의 생성자를 먼저 실행합니다.

Runnable () : thread(NULL) {
    pthread_create(&thread, NULL, Runnable::Run, this);
}

위 코드에서 Runnable의 생성자가 스레드를 생성하고이 스레드의 함수가 Worker 클래스를 다시 작성했기 때문에 Worker :: Run ()을 실행하는 것을 볼 수 있습니다. 스레드가 생성 된 후 프로그램은 Worker의 생성자를 실행하기 시작합니다. 즉, quit를 false로 설정합니다. 문제는 여기에 있습니다. 쓰레드가 quit 초기화보다 우선 순위를 가지고 있습니다. 특정 확률로 스레드 함수는 quit 실행 후 초기화를 완료하지 못했습니다. 그 결과 스레드 함수가 여전히 남아 있기 때문에 스레드 함수가 직접 종료됩니다. 종료 값을 판단 할 때 임의의 값. 프로그램은 스레드가 생성되었다고 생각하지만 스레드 함수가 종료되고 외부 표시가 죽었습니다.

       위에서 언급 한 버그 외에도 Worker destructuring에도 문제가 있습니다. C ++ destructuring의 순서는 먼저 하위 클래스의 소멸자를 실행 한 다음 부모 클래스의 소멸자를 실행하는 것입니다. 이로 인해 발생하는 문제는 Worker 객체가 이미 소멸자를 실행했지만 스레드 함수는 여전히 실행 중일 수 있다는 것입니다. 이때 스레드 함수가 Worker 객체의 멤버 변수를 조작하면 붕괴 될 위험이 있습니다.

       겉보기에 단순한 코드이지만 세부 사항이 적기 때문에 특히 CPU 코어 수가 적은 워크 스테이션에서 BUG를 찾기가 어렵습니다. 코어 수가 많은 서버에서는 생성 된 스레드를 즉시 실행할 수 있지만 코어 수가 적 으면 생성 된 스레드가 타임 슬라이스가 없기 때문에 부모 스레드보다 뒤쳐져 감지하기 어렵습니다. 버그.

추가 질문

클래스에는 여러 개의 멤버 변수가 있으며 멤버 변수의 초기화 순서는 생성자에서 할당 된 순서가 아닌 멤버 변수가 정의 될 때 결정됩니다. 인터넷에서도 유사한 서면 테스트 문제가 있음을 기억하십시오.

class Type {
public:
    Type() :b(2), a(1){
    }

private:
    int a;
    int b;
};

위 코드의 실제 실행 순서는 a에 값 1을 할당하고 b에 값 2를 할당하는 것입니다.

추천

출처blog.csdn.net/weixin_42663840/article/details/89462951