정의
싱글톤 모드의 원래 의도는 전체 런타임 및 런타임 공간에서 특정 데이터 유형의 고유한 인스턴스가 하나만 있는지 확인하고 전역 액세스 인터페이스를 제공하는 것입니다.
인스턴스 생성 및 액세스의 두 가지 관점에서 싱글톤 패턴을 더 깊이 이해할 수 있습니다. 유형의 인스턴스 정의 및 생성을 제한하는 거세 데이터 유형입니다. (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 구현을 소개합니다. 다음 단원에서는 싱글톤의 게으른 구현에 대해 계속 논의할 것입니다.