C言語:シングルトンモード(腹ペコ中国語風)

意味

シングルトン モードの本来の目的は、ランタイムおよびランタイム空間全体に特定のデータ型の一意のインスタンスが 1 つだけ存在することを保証し、グローバル アクセス インターフェイスを提供することです。

インスタンスの作成とアクセスの 2 つの観点から、シングルトン パターンをより深く理解できます: (1) データ型にはインスタンスが 1 つだけあり、プログラマは通常のデータ型のようにこの型のインスタンスを自由に定義できません。これは、型のインスタンスの定義と作成を制限する去勢されたデータ型です。(2) このアクセスインターフェースは、グローバルにユニークなインスタンスのアクセスインターフェースであり、通常の意味でのデータアクセスインターフェースではありません。

達成

一般的なシングルトンモードは、オブジェクトの生成と呼び出しのタイミング関係によってレイジータイプとハングリータイプに分けられます。通常、Hungry 型はプログラムの開始時にオブジェクトを作成し、遅延初期化されません。Lazy 型は、実際に使用されるときに遅延初期化を使用して作成されます。

ハングリースタイル

Hungry スタイルは、Hungry と同様に、プログラムの開始時に、必要かどうかに関係なく作成されます。C++ では、Hungry スタイルを実装するためにグローバル変数として宣言するのが一般的で、グローバル変数は main 関数の実行前にグローバル変数オブジェクトを作成し、main 関数の実行終了後にグローバル変数を解放します。

実行可能プログラムにはグローバル _CTOR_LIST 関数ポインタ配列があり、コンパイラはリンク時にグローバル変数のコンストラクタ ポインタを _CTOR_LIST に追加します。その後、実行可能プログラムは main 関数を実行する前に _CTOR_LIST をトラバースして実行します。すべての関数ポインタは、したがってグローバル変数の構築が完了しました。

同じ実行可能プログラムにはグローバル _DTOR_LIST 関数ポインタ配列もあり、コンパイラはリンク時にグローバル変数のデストラクタ ポインタを _DTOR_LIST に追加します。実行可能プログラムは main 関数を実行した後、この _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 はコード セグメント名を宣言することによって実装します。2 つの特別なセグメント名「.CRT XIU」、「.CRT XIU」、「.CRT」を宣言します。XIU _ _" .C R T XCU" を指定すると、リンカは".CRT XIU" セクションで宣言された関数を "C 初期化関数テーブル" に配置し、また、".CRT XIU" セクションで宣言された関数を" C初期化関数テーブル」でも「.CRT」で宣言されます。「X I U セクション関数C初期化関数テーブル配置され、 「. C R T XCU セクションで宣言された関数「C++ 初期化関数テーブル配置されます。MSVC実行可能プログラムは、最初にmain 関数を実行する前に、「C 初期化関数テーブル」と「C++ 初期化関数テーブル」を走査し、関数を順番に実行します。main 関数の実行後、_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 メイン関数の前後で関数を実行する原理分析に基づいて、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;
}

要約する

シングルトン モードは、ランタイムおよびランタイム空間全体に特定のデータ型の一意のインスタンスが 1 つだけ存在することを保証し、グローバル アクセス インターフェイスを提供します。この記事では、シングルトンの目的とシングルトンのハングリー実装について紹介します。次の単元では、引き続きシングルトンの遅延実装について説明します。

おすすめ

転載: blog.csdn.net/liuguang841118/article/details/124898593