C 언어: 싱글톤 모드(배고픈 중국 스타일)

정의

싱글톤 모드의 원래 의도는 전체 런타임 및 런타임 공간에서 특정 데이터 유형의 고유한 인스턴스가 하나만 있는지 확인하고 전역 액세스 인터페이스를 제공하는 것입니다.

인스턴스 생성 및 액세스의 두 가지 관점에서 싱글톤 패턴을 더 깊이 이해할 수 있습니다. 유형의 인스턴스 정의 및 생성을 제한하는 거세 데이터 유형입니다. (2) 이 액세스 인터페이스는 일반적인 의미의 데이터 액세스 인터페이스가 아니라 전역적으로 고유한 인스턴스의 액세스 인터페이스입니다.

성취하다

일반적인 싱글톤 모드는 객체 생성과 호출의 타이밍 관계에 따라 게으른(lazy) 타입과 배고픈(hungry) 타입으로 나눌 수 있다. Hungry 유형은 일반적으로 프로그램 시작 시 객체를 생성하며 지연 초기화되지 않으며, 지연 유형은 지연 초기화를 사용하여 실제 사용 시 생성됩니다.

배고픈 스타일

Hungry와 마찬가지로 Hungry 스타일은 필요 여부와 관계없이 프로그램이 시작될 때 생성됩니다. C++에서는 일반적으로 굶주린 스타일을 구현하기 위해 전역 변수로 선언하는데, 전역 변수는 main 함수 실행 전에 전역 변수 객체를 생성하고, main 함수 실행이 끝나면 전역 변수를 해제합니다.

실행 프로그램에는 전역 _CTOR_LIST 함수 포인터 배열이 있으며, 컴파일러는 연결 시 전역 변수의 생성자 포인터를 _CTOR_LIST에 추가합니다. 전역 변수 구성 완료.

동일한 실행 프로그램에는 전역 _DTOR_LIST 함수 포인터 배열도 있으며, 컴파일러는 연결 시 전역 변수의 소멸자 포인터를 _DTOR_LIST에 추가합니다. 실행 프로그램이 주 함수를 실행한 후 이 _DTOR_LIST를 순회하고 실행합니다. 따라서 전역 변수의 소멸을 완료합니다.

C++ 배고픈 중국식 전역 변수의 구성 과정에 익숙하므로 전역 변수 원칙의 구성 원리를 참조하여 C 언어 배고픈 중국식을 구현합니다. 다행스럽게도 GCC와 MSVC 모두 main 전후에 함수를 호출하는 해당 메커니즘을 제공합니다.

GCC

GCC는 속성 키워드를 사용하여 생성자 및 소멸자 C 함수를 선언할 수 있습니다. 생성자로 선언된 함수는 main 이전에 호출되고 소멸자로 선언된 함수는 main 이후에 호출됩니다.

#include<stdio.h> 

// 声明一个constructor属性函数,此函数会在main之前运行
__attribute__((constructor)) void before() 
{
    
      
   printf("run function before main\n"); 
} 

// 声明一个destructor属性函数,此函数会在main之后运行
__attribute__((destructor)) void after() 
{
    
     
   printf("run function after main\n"); 
} 
  
int main(int argc, char **argv) 
{
    
     
   printf("run main function\n"); 
   return 0; 
}

GCC 메인 함수보다 먼저 함수를 실행하는 원리를 참고하여 싱글톤 버전의 GCC 버전을 구현할 수 있습니다.

싱글톤 선언

#pragma once

#include <stdio.h>
#include <stdlib.h>

typedef  void  File;

typedef enum BOOL
{
    
    
	FALSE = 0,
	TRUE = 1,
}BOOL;

typedef struct tagFileManager
{
    
    
	File* (*mkFile)(const char* fileName, char const* mode);
	BOOL  (*rmFile)(const char*  fileName);
	int   (*write)(File* file, const char* buf, int size);
	BOOL  (*exit)(const char* fileName);
	BOOL  (*close)(const File* file);
}FileManager;

싱글톤 구현

#include "file_manager.h"

#include <io.h>

static FileManager g_fileManager;

typedef int constructor();

static File* mkFile(const char* fileName, char const* mode);
static BOOL rmFile(const char* fileName);
static int fileWrite(File* file, const char* buf, int size);
static BOOL isExit(const char* fileName);
static BOOL fileClose(const File* file);

File* mkFile(const char* fileName, char const* mode)
{
    
    
	FILE* file = NULL;
	if (0 == fopen_s(&file, fileName, mode))
	{
    
    
		return file;
	}
	
	return NULL;
}

BOOL rmFile(const char* fileName)
{
    
    
	if (isExit(fileName))
	{
    
    
		return !remove(fileName);
	}
}

int fileWrite(File* file, const char* buf, int size)
{
    
    
	return fwrite(buf, size, 1, file);
}

BOOL isExit(const char* fileName)
{
    
    
	return (_access(fileName, 0) == 0);
}

BOOL fileClose(const File* file)
{
    
    
	return !fclose(file);
}

__attribute__((constructor)) static int ctor()
{
    
    
	g_fileManager.exit = isExit;
	g_fileManager.mkFile = mkFile;
	g_fileManager.rmFile = rmFile;
	g_fileManager.write = fileWrite;
	g_fileManager.close = fileClose;
	return 0;
}

__attribute__((destructor)) static int dtor()
{
    
    
	g_fileManager.exit = NULL;
	g_fileManager.mkFile = NULL;
	g_fileManager.rmFile = NULL;
	g_fileManager.write = NULL;
	g_fileManager.close = NULL;
	return 0;
}

FileManager* fileManager()
{
    
    
	return &g_fileManager;
}

MSVC

MSVC는 코드 세그먼트 이름을 선언하여 구현하며 두 개의 특수 세그먼트 이름 ".CRT XIU", ".CRT XIU", ".CRT를 선언합니다.엑스 아이유 " , _" . C R T XCU", 링커는".CRT XIU" 섹션에서 선언된 함수를 "C 초기화 함수 테이블"에 넣고 ". CRT XIU" 섹션에서 선언된 함수를 " C 초기화 함수 테이블", ".CRT"에도 선언됩니다." X I U " 섹션 함수는 " C 초기화 함수 테이블 " 에 배치되고 " . C R T XCU " 섹션 에서 선언된 함수 "C++ 초기화 함수 테이블 " 배치 됩니다 . MSVC 실행 프로그램은 먼저 main함수를 실행하기 전에 "C초기화함수표"와 "C++초기화함수표"를 훑어보면서 차례대로 함수를 실행하고, 주함수를 실행한 후에는 _onexit()를 통해 등록해야 한다.

#include <stdlib.h>

int before1()
{
    
    
    printf("run function before1 before main\n"); 
    return 0;
}

int before2()
{
    
    
    printf("run function before2 before main\n");  
    return 0;
}

int after()
{
    
    
    printf("run function after main\n");
    return 0;
}

typedef int constructor();

#pragma data_seg(".CRT$XIU")
static constructor *beforeTab1[] = {
    
    before1}; 
#pragma data_seg(".CRT$XCU") 
static constructor *beforeTab2[] = {
    
    before2};

#pragma data_seg()

int _tmain(int argc, _TCHAR *argv[])
{
    
    
    _onexit(after);
    printf("run main function\n");
    return 0;
}

MSVC 메인 함수 전후에 함수를 실행하는 원리 분석을 바탕으로 Hungry Singleton Mode의 MSVC 버전을 작성할 수 있습니다.

싱글톤 선언

#pragma once

#include <stdio.h>
#include <stdlib.h>

typedef  void  File;

typedef enum BOOL
{
    
    
	FALSE = 0,
	TRUE = 1,
}BOOL;

typedef struct tagFileManager
{
    
    
	File* (*mkFile)(const char* fileName, char const* mode);
	BOOL  (*rmFile)(const char*  fileName);
	int   (*write)(File* file, const char* buf, int size);
	BOOL  (*exit)(const char* fileName);
	BOOL  (*close)(const File* file);
}FileManager;

FileManager* fileManager();

싱글톤 구현

#include "file_manager.h"

#include <io.h>

static FileManager g_fileManager;

typedef int constructor();

static File* mkFile(const char* fileName, char const* mode);
static BOOL rmFile(const char* fileName);
static int fileWrite(File* file, const char* buf, int size);
static BOOL isExit(const char* fileName);
static BOOL fileClose(const File* file);
static int ctor();
static int dtor();

File* mkFile(const char* fileName, char const* mode)
{
    
    
	FILE* file = NULL;
	if (0 == fopen_s(&file, fileName, mode))
	{
    
    
		return file;
	}
	
	return NULL;
}

BOOL rmFile(const char* fileName)
{
    
    
	if (isExit(fileName))
	{
    
    
		return !remove(fileName);
	}
}

int fileWrite(File* file, const char* buf, int size)
{
    
    
	return fwrite(buf, size, 1, file);
}

BOOL isExit(const char* fileName)
{
    
    
	return (_access(fileName, 0) == 0);
}

BOOL fileClose(const File* file)
{
    
    
	return !fclose(file);
}

static int ctor()
{
    
    
	g_fileManager.exit = isExit;
	g_fileManager.mkFile = mkFile;
	g_fileManager.rmFile = rmFile;
	g_fileManager.write = fileWrite;
	g_fileManager.close = fileClose;

	_onexit(dtor);

	return 0;
}

static int dtor()
{
    
    
	g_fileManager.exit = NULL;
	g_fileManager.mkFile = NULL;
	g_fileManager.rmFile = NULL;
	g_fileManager.write = NULL;
	g_fileManager.close = NULL;
	return 0;
}

#pragma data_seg(".CRT$XIU")
static constructor * before[] = {
    
     ctor };
#pragma data_seg()


FileManager* fileManager()
{
    
    
	return &g_fileManager;
}

요약하다

싱글톤 모드는 전체 런타임 및 런타임 공간에서 특정 데이터 유형의 고유한 인스턴스가 하나만 있는지 확인하고 전역 액세스 인터페이스를 제공하는 것입니다. 이 기사에서는 싱글톤의 목적과 싱글톤의 Hungry 구현을 소개합니다. 다음 단원에서는 싱글톤의 게으른 구현에 대해 계속 논의할 것입니다.

추천

출처blog.csdn.net/liuguang841118/article/details/124898593