可変引数関数はどのようにオブジェクトを構造体/クラスに取りますか?例えば :
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
これは、このオブジェクトCString::Format
を受け取るprintfスタイルの変数引数関数です。CString
これはどのように可能ですか?
差出人:http ://msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx
- constchar*およびLPCTSTR関数の引数をCStringオブジェクトに自由に置き換えることができます。
おそらくCString
、vtableがなく、型の属性のみを持つクラスchar*
です。これは、 aをaにsizeof(CString) == sizeof(const char*)
reinterpret_castすると、動作することを意味します。CString
const char*
const char*
コンパイラは、関数の可変個引数部分への構造体の受け渡しを受け入れてはなりません。しかし、私が間違っていなければ、GCCで、可変引数として構造体を渡すと、そのメモリがコピーされ(つまり、コピーコンストラクターは使用されません)、警告が発行されます。私はMSVCがそこで同じことをしていると思います、そしてそれから、Format
メソッドは与えられたデータがであると仮定しているだけconst char*
です。
別の例を挙げましょう。メンバーのみがintであるvtableのないクラスがあるとします。これを可変個引数に渡すと、コンパイラはこのオブジェクトの内容をコピーします。これはintのみです。関数の実装では、(どういうわけか)自分がを受け取ったことを知っていますint
。次に、を要求するパラメータをクエリしますint
。メモリレベルでは、クラスはと何ら変わりはないのでint
、物事は「うまく」機能します。関数はint
クラスの属性にアクセスします。
以下にあるMFCコードを少し調べてデバッグした後、これまでに有名な静的コードアナライザーエラー「構造体'CStringT'を省略記号に渡す」に直面した人に役立つことを願っています。これは実際にも私の疑問の原因です。
http://www.gimpel.com/html/bugs/bug437.htm
可変個引数関数であるフォーマット関数は、最初のパラメーターに存在するフォーマット指定子に依存します。最初のパラメーターは常にchar*です。
フォーマット指定子(%s、%d、%i…)を解析し、見つかったフォーマット指定子のインデックスに基づいてvar_arg配列を読み取り、%sの場合はchar *に、%dが指定されている場合はintに直接キャストします。
したがって、CStringが指定され、対応するフォーマット指定子が%sの場合、char*への直接キャストがCStringオブジェクトに対して行われます。
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
wcout<<a_csName;
私の年齢は20です
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %d"),a_csAge);
wcout<<a_csName;
私の年齢は052134です
したがって、この背後にある知性はありません。ただの直接キャスト。したがって、PODまたはユーザー定義データ構造を渡しますが、違いはありません。
私は最近、このLintエラーをクリアする必要があり、このスレッドに遭遇しました。
発生しているキャストの興味深い調査ですが、Lint警告を回避する最も簡単な方法はGet.String()
、次のようにCStringメソッドを使用してCString構造体ではなく、CStringポインターを渡すことです。
a_csName.Format(_T("My Age is : %d"), a_csAge.GetString());
これに別の方法で答えさせてください。
struct Value
{
int nValue;
float fOtherValue;
};
int main()
{
Value theValue;
theValue.nValue = 220;
theValue.fOtherValue = 3.14f;
printf("Value: %d", theValue);
}
このコードは220
問題なく印刷されます。しかし、の後に2番目の引数を渡すと、次のようにはtheValue
なりません。
printf("Values: %d, %f", theValue, theValue.fOtherValue);
最初の変数引数が引数theValue
で指定されたサイズを満たしていないため%d
。したがって、3.14
表示されない(表示されない場合があります)。printf
スタックをプッシュする引数などがどのように機能するかについては話さないでくださいva_start
。
同様に、String
このようにクラスを設計すると、次のようになります。
struct String
{
char* pData;
public:
String()
{
pData = new char[40];
strcpy_s(pData, 40, "Sample");
}
};
そしてそれをこのように使用します:
String str;
printf("%s", str);
それは間違いなく機能します。
しかし、引数とコールスタックの破損についてはどうでしょうか?クラスのサイズ(のような)がformat()で指定されValue
たデータサイズ(の場合は4)より大きい場合はどうなりますか。その場合、設計されたクラスは、指定されたクラスのサイズが関数で使用されている引数のサイズと同じであることを確認する必要があります。Value
%d
printf
詳細については触れませんが、CString
内部クラスを使用しCStringData
ます。後者のクラスは、文字列、長さ、参照カウントなどを保持します。CString
(実際には)このクラスを使用しますが、。によって管理される/CSimpleStringT
のポインタのみを保持します。char
wchar_t
CStringData
主に、言及CString
されたクラスとして実装されます(データと行くString
限り)。sizeof