1. ホーム

__declspec の使用法の詳細

2022-03-02 11:31:22

__declspec は、与えられた型のインスタンスの Microsoft 関連のストレージを指定するために使用されます。static や extern などの他の格納方法の修飾子は、C および C++ 言語の ANSI 仕様であるのに対し、__declspec は拡張属性の定義です。拡張属性の構文は、Microsoftの拡張機能に関してCおよびC++言語を簡略化し、標準化するものです。
使用方法

__declspec (extended-decl-moderator)

extended-decl-modifierの引数は以下の通りであり、空白で区切って同時に出現させることができる。

  • align (C++)
  • アロケート
  • アプリドメイン
  • 非推奨 (C++)
  • dllimport
  • dllexport
  • ジットリンシック
  • ネイキッド(C++)
  • ノアリア
  • ノインライン
  • ノーリーターン
  • nothrow (C++)
  • 新型
  • プロセス
  • プロパティ (C++)
  • 制限する
  • セレクタニー
  • スレッド
  • uuid (C++)

1. キーワード __declspec は単純な宣言の前に出現させる必要があります。コンパイラは、変数宣言の * や & の後、あるいは識別子の前に現れる __declspec を無視し、警告を与えません。

2. 2. __declspec が変更するのは型なのか変数なのかの区別に注意してください。

__declspec(align(8)) struct Str b;

この修正は変数bに対するもので、他の場所で定義されたstruct Str型の変数は__declspec(align(8))によって影響を受けません。

__declspec(align(8)) struct Str {};

修飾子は struct Str 型です。この型の変数はすべて __declspec(align(8)) の影響を受けます。

使用方法

  • 整列

フォーマット

__declspec(align(n)) declarator

nはアライメントパラメータで、その有効値は2の整数乗(1〜8192バイト)、例えば2、4、8、16、32、64である。パラメータ宣言子は、アライメントを設定するデータである。
1. 1. __declspec(align(n)) を使用して、ユーザー定義データのアライメントを正確に制御します。構造体、ユニオン、クラスを定義するとき、または変数を宣言するときに __declspec(align(n)) を使用することができます。
2. 関数の引数に __declspec(align(n)) を使用することはできません。
3. 3. __declspec(align(#)) を使用しない場合、コンパイラはデータのサイズに応じた自然な境界で整列します。例えば、4バイトの整数は4バイトの境界線に、8バイトのdoubleは8バイトの境界線に整列されます。クラスや構造体のデータは、データ自体のナチュラルアライメントと、#pragma pack(n)で設定されたアライメント係数のうち最も小さいものを取ってアライメントされます。
4. __declspec(align(n)) と #pragma pack(n) は兄弟で、前者はアライメント係数の最小値、後者はアライメント係数の最大値を指定します。
5. 両方が存在する場合、前者がより優先される。すなわち、両方が存在し、矛盾する値を持つ場合、後者は機能しない。
6. この __declspec(align(n)) の指定は、変数サイズが #pragma pack(n) で指定した n 以上で、かつ __declspec(align(n)) で指定した値 n が対応する型長より小さい場合、機能しないことに注意してください。
7. 7. #pragma pack(n) で指定する値 n が全データメンバのサイズ以上である場合、この値 n は機能しない。

  • アロケート

フォーマット

__declspec(allocate("segname")) declarator

データを格納するデータセグメントを指定します。データセグメント名は、以下の列挙のいずれかでなければならない。
コードセグ
const_seg
データセグ
init_seg
セクション

  • アプリドメイン

管理対象アプリケーションの各アプリケーションドメインが、指定されたグローバル変数または静的メンバ変数のコピーを持つことを指定します。

  • 非推奨

pragma deprecated()と同じ働きをします。関数のオーバーロード形式を指定することは推奨されません。プログラム内で非推奨の関数が呼び出された場合、コンパイラはC4996警告を出し、特定の警告メッセージを指定することができます。警告メッセージは、定義されたマクロから派生させることができます。
例えば

// compile with: /W3
#define MY_TEXT "function is deprecated"
void func1(void) {}
__declspec(deprecated) void func1(int) {}
__declspec(deprecated("** this is a deprecated function **")) void func2(int) {}
__declspec(deprecated(MY_TEXT)) void func3(int) {}

int main() {
   func1();
   func1(1); // C4996, warning message: warning C4996: 'func1': was declared deprecated
   func2(1); // C4996, warning C4996: 'func2': ** this is a deprecated function **
   func3(1); // C4996, warning C4996: 'func3': function is deprecated
}

  • dllimport, dllexport

フォーマット

__declspec(dllimport) declarator
__declspec(dllexport) declarator

はそれぞれ、関数、データ、オブジェクトをDLLからインポートするため、また関数、データ、オブジェクトをDLLからエクスポートするために使用されます。これは、DLLのインターフェイスを定義することと同じで、そのクライアントのexeやDLLで使用できる関数、データまたはオブジェクトを定義します。
関数をdllexportとして宣言することで、モジュール定義(.DEF)ファイルを定義する必要がなくなります。
dllexport は __export キーワードを置き換えます。
dllexport と宣言された C++ 関数は、エクスポートされる際に C++ の規則に従って関数名が処理されます。C++の規則に従って名前が処理されないようにする必要がある場合は、.defファイルを使用するか、extern "C"を使用します。

  • jitintrinsic

フォーマット

__declspec(jitintrinsic)

関数または要素を 64 ビット CLR (Common Language Runtime) としてマークするために使用します。主にマイクロソフトが提供する特定のライブラリで使用される。jitintrinsic を使用すると、関数のシグネチャに MODOPT (IsJitIntrinsic) が追加されます。

フォーマット

__declspec(naked) declarator

このキーワードは、x86システムでのみ使用され、主に仮想デバイスドライバに使用されます。このキーワードを使用すると、コンパイラはコメントやマークアップのないコードを生成することができます。このキーワードは関数定義にのみ使用でき、データ宣言、定義、関数宣言には使用できません。

  • ノアリアス

関数にのみ適用され、その関数が半純粋であることを示します。準純関数とは、ローカル変数、引数、第一レベル間接引数のみを参照または変更する関数のことです。これは、もし関数がグローバル変数や第2レベルの間接ポインターの引数を参照した場合、コンパイラはアプリケーションを破壊するコードを生成することを約束するものです。

  • 制限する

フォーマット

__declspec(restrict) return_type f();

CRTのmalloc関数など、ポインタを返す関数の宣言や定義にのみ適用されます。

__declspec(restrict) void *malloc(size_t size);

これは、この関数が返すポインタが他のポインタと混同されないことをコンパイラに伝えるものです。これは、コンパイラに最適化を行うための情報を提供するものです。コンパイラにとって最大の難関のひとつは、どのポインタが他のポインタと混同されるかを判断することであり、この情報を利用することはコンパイラにとって有用である。注意しなければならないのは、これはコンパイラとの約束事であり、コンパイラはそれを検証していないことです。もしプログラムが __declspec(restrict) を不適切に使用した場合、プログラムは正しくない動作をすることになります。

  • ノーインライン

クラス定義で定義されたメンバ関数は、デフォルトですべてインライン化されているため、クラス内の関数をインライン化する必要がないことを明示的に指定するために __declspec(naked) を使用します。関数が小さく、システムの性能にほとんど影響を与えない場合は、インラインでないことを宣言することが必要である。例えば、エラー発生時の処理に使われる関数などです。

  • ノーリーターン

関数が __declspec(noreturn) で変更された場合、その関数は戻らないことをコンパイラに伝えることを意味し、その結果コンパイラは __declspec(noreturn) として変更された関数以降のコードが到達不可能であることを知ることになります。
もしコンパイラが戻り値のないコードの分岐を持つ関数を見つけた場合、コンパイラは C4715 警告、または C2202 エラーメッセージを報告します。関数が返さないために分岐が到達できない場合、__declspec(noreturn)規約を使用することでこれらの警告やエラーを回避することができます。
戻り値が期待される関数に対して __declspec(noreturn) という規約を適用すると、未定義の動作が発生します。
この下の例では、main関数はelseブランチから戻らないので、コンパイルや警告メッセージを避けるために、関数fatalは__declspec(noreturn)で合意されています。

__declspec(noreturn) extern void fatal () {}
int main()
{
    if(1) return 1;
    else if(0) return 0;
    else fatal();
}

  • ノットスロー

フォーマット

return-type __declspec(nothrow) [call-convention] function-name ([argument-list])

関数宣言に使用できる。宣言された関数や、その関数の内部で呼び出された他の関数が例外を投げないことをコンパイラに知らせます。

  • 新参者

はどのクラス宣言でも使用できますが、クラス自体がインスタンス化されることのない純粋なインターフェイスクラスにのみ使用するのが最も適しています。このキーワードを宣言すると、コンパイラはコンストラクタとデストラクタの vfptr を初期化しなくなります。コンパイルされたコードのサイズを最適化することができます。
__declspec(novtable) で宣言されたクラスをインスタンス化し、そのクラスのメンバーにアクセスしようとすると、実行時にアクセス違反 (AV) が発生します。

  • 処理

は、管理対象のアプリケーションプロセスが、すべてのアプリケーションドメインで共有される指定されたグローバル変数、静的メンバ変数、または静的ローカル変数のコピーを持っている必要があることを示します。clr:pureでコンパイルする場合、デフォルトで各アプリケーションドメインがグローバル変数と静的変数のコピーを持つため、__declspec(process)を使用する必要があります。clr でコンパイルする場合は、__declspec(process) を使用する必要はありません。なぜなら、デフォルトでは、/clr でコンパイルする場合、各プロセスはグローバル変数とスタティック変数のコピーを持っているからです。
__declspec(process) で変更できるのは、グローバル変数、静的メンバ変数、またはローカル型の静的変数だけです。
clr:pure でコンパイルする場合、__declspec(process) で宣言された変数も const 型として宣言する必要があります。
各アプリケーション・ドメインにグローバル変数のコピーを持たせたい場合は、appdomainを使用します。

  • プロパティ

形式です。

__declspec(property(get=get_func_name)) declarator
__declspec(property(put=put_func_name)) declarator
__declspec(property(get=get_func_name, put=put_func_name)) declarator

このプロパティは、クラスや構造体定義の中で、非静的なダミーデータのメンバーとして使用することができます。getとputはプロパティアクセスのための権限で、1つは読み込み、もう1つは書き込みのためのものです。コンパイラは、プロパティによって変更されたデータ・メンバがメンバ・セレクタの右側に表示される(". " または "->" )のを見ると、その操作を get または put メソッドに変換してくれます。この修飾子は、クラスや構造体の定義で空の配列に使用することもできます。
使い方は以下の通りです。

struct S
{
    int i;
    void putprop(int j)
    {
        i = j;
    }
    int getprop()
    {
        return i;
    }

    __declspec(property(get = getprop, put = putprop)) int the_prop;

};

int main()
{
    S s;
    s.the_prop = 5;
    return s.the_prop;
}

  • セレクタニー

フォーマット

__declspec(selectany) declarator

MFC、ATLのソースコードには、__declspec(selectany)宣言がたくさんあります。selectanyを使うと、グローバル変数の初期化を.cppだけでなく.hファイルでも行うことができます。例えば、static変数を持つクラスがあったとして、それを.hで初期化するには、次のようにします。

__declspec(selectany) type class::variable = value;

これは、グローバル変数を初期化するコードです。.hが複数回インクルードされていても、リンカが多重定義エラーを除外してくれます。これは、テンプレートをプログラミングする際に非常に便利です。
使い方は以下の通りです。

__declspec(selectany) int x1 = 1; //correct, x1 is initialized and visible to the outside
const __declspec(selectany) int x2 =2; //incorrect, const is static by default in C++; but correct in C, whose default const is not static
extern const __declspec(selectany) int x3 = 3; //correct, x3 is an extern const, visible to the outside
extern const int x4;
const __declspec(selectany) int x4=4; //correct, x4 is an extern const, visible to the outside
extern __declspec(selectany) int x5; //error, x5 is not initialized and cannot be modified with __declspec(selectany)

class X
{
public:
    X(int i){i++;};
    int i;
};
__declspec(selectany) X x(1); //correct, dynamic initialization of global object

  • スレッド

フォーマット

__declspec(thread) declarator

スレッドストレージの時間制限を持つスレッドローカル変数として declarator を宣言し、スレッド作成時にストレージが自動的に割り当てられるようにリンカが手配できるようにします。
スレッドローカルストレージ(TLS)は、マルチスレッドの実行環境において、各スレッドが独自のローカルデータを確保する仕組みである。通常のマルチスレッドプログラムでは、複数のスレッドでデータを共有するが、TLSは各スレッドに独自のローカルデータを割り当てるための機構である。
この属性は、データまたはメンバ関数を含まないクラスの宣言と定義にのみ使用でき、関数の宣言と定義には使用できない。
この属性の使用は、DLLの遅延ロードに影響を与える可能性があります。
この属性は、グローバルデータオブジェクト(staticおよびextern)、ローカルスタティックオブジェクト、およびクラスのスタティックデータメンバーを含むスタティックデータにのみ使用でき、自動データオブジェクトには使用できません。
この属性は、データの宣言と定義の両方について、単一のファイルまたは複数のファイルにかかわらず使用する必要があります。
__declspec(thread) は、型修飾子として使用できません。
クラス宣言と共にオブジェクトが定義されていない場合、 __declspec(thread) は無視されます、例えば。

// compile with: /LD
__declspec(thread) class X
{
public:
    int I;
} x; //x is a thread object
X y; //y is not a thread object

次の2つの例は、意味的に同じである。

__declspec(thread) class B
{
public:
    int data;
} BObject; //BObject is the thread object

class B2
{
public:
    int data;
};
__declspec(thread) B2 BObject2; // BObject2 is the thread object

  • uuid

フォーマット

__declspec(uuid("ComObjectGUID")) declarator

一意な識別子番号を持つ登録コンテンツを変数として宣言し、__uuidof()で呼び出すことができるようにします。
使い方は以下の通りです。

struct __declspec(uuid("00000000-0000-0000-0000-c000-000000000046")) IUnknown;
struct __declspec(uuid("{00020400-0000-0000-0000-c000-000000000046}")) IDispatch;


使用例

インターフェイスの定義

#include <IOSTREAM>
using namespace std;

#define interface class __declspec(novtable)

interface ICodec
{
public:
    virtual bool Decode(char * lpDataSrc, unsigned int nSrcLen, char * lpDataDst, unsigned int *pnDstLen);
    virtual bool Encode(char * lpDataSrc, unsigned int nSrcLen, char * lpDataDst, unsigned int *pnDstLen);
};

ICodecは以下と同等です。

class ICodec
{
public:
    virtual bool Decode(char * lpDataSrc, unsigned int nSrcLen, char * lpDataDst, unsigned int *pnDstLen) = 0;
    virtual bool Encode(char * lpDataSrc, unsigned int nSrcLen, char * lpDataDst, unsigned int *pnDstLen)=0;
};

クラスのプロパティを定義する

オブジェクト指向プログラミングに欠かせない要素であるプロパティは、広義にはオブジェクトの状態を表すために使われます。今回は狭義のプロパティについて、"="演算子でクラスのデータを取得・設定する権利を制御できることを意味します。

#include <IOSTREAM>
#include <map>
#include <string>
#include <CONIO.H>
using namespace std;
 
class propertytest
{
    int m_xvalue;
    int m_yvalues[100];
    map<string,string> m_zvalues;

public:
    __declspec(property(get=GetX, put=PutX)) int x;
    __declspec(property(get=GetY, put=PutY)) int y[];
    __declspec(property(get=GetZ, put=PutZ)) int z[];

    int GetX()
    {
        return m_xvalue;
    };

    void PutX(int x)
    {
        m_xvalue = x;
    };

    int GetY(int n)
    {
        return m_yvalues[n];
    };

    void PutY(int n,int y)
    {
        m_yvalues[n] = y;
    };

    string GetZ(string key)
    {
        return m_zvalues[key];
    };

    void PutZ(string key,string z)
    {
        m_zvalues[key] = z;
    };

};

int main(int argc, char* argv[])
{
    propertytest test;
    test.x = 3;
    test.y[3] = 4;
    test.z["aaa"] = "aaa";
    std::cout << test.x <<std::endl;
    std::cout << test.y[3] <<std::endl;
    std::cout << test.z["aaa"] <<std::endl;

    getch();
    return 0;
}

ディル

_declspec(dllimport) は、この関数が他の DLL からインポートされていることを意味しています。
_declspec(dllexport) は、この関数がこのDLLからエクスポートされることを意味しています。

#define Test_API __declspec(dllexport)

class test
class test
    public:
    Test_API HRESULT WINAPI Initialize(LPCTSTR filename);
}