Microsoft Visual C++    製品情報    |    検索  |    サポート  |    フィードバック   |   ホーム  
Microsoft
   Visual C++ ホームページ   |   Visual Studio   |   開発関連製品   |   MSDN online   |   各国の開発者用サイト   |
  製品のご案内
  Visual Studio.NET
  技術ドキュメント
  サポート情報
  よくある質問
  ダウンロード
  登録とオーナーエリア
  関連情報
 


banner
Member of Visual Studio 技術ドキュメント

Microsoft Visual C++ 6.0 移行ガイド

Microsoft Visual C++ チーム
Microsoft Corporation

1998 年 8 月 28 日

要約: Visual C++ 6.0 の新機能について、技術的に詳しく解説します。Visual C++ に関する知識が必要です。以下のトピックを扱っています。

Microsoft Visual C++ version 6.0 開発システムには、開発者の生産性を高め、実行可能ファイルのパフォーマンスを改善し、きわめて細かい制御を可能にする新しい機能が多数含まれています。しかし、Visual C++ 6.0 に移行する際には、プロダクトに加えられた変更点を慎重に分析し、開発者のコードと開発サイクルに与える影響を検討することが重要です。このガイドは、特に開発者が Visual C++ 6.0 をフルに活用できるようにするということを目標に、コンパイラ、リンカ、およびデバッガに焦点を当てて、Microsoft Visual C++ version 6.0 開発システムへの移行を支援します。

    注: 以前の32ビット バージョンの Visual C++ で作られたプロジェクトは、Visual C++ 6.0 で自動的に開き、Visual C++ 6.0 プロジェクトとして保存することができます。詳細については、『Visual C++ プログラマーズ ガイド』の「移植とアップグレード」の項を参照してください(VisualC++に付属する MSDN ライブラリに含まれています)。

開発環境とエディタの変更点

IntelliSense テクノロジ
Microsoft IntelliSense テクノロジ (IntelliSense またはステートメントの補完とも呼ばれます) により、エディタの中でCまたは C++ のソース コードを書いているときに、各種の定義に簡単にアクセスすることができます。関数名、パラメータ、およびコメントが表示されます。Visual C++ が自動的に補完できるような文字(変数名の後の "." や "->"、関数名の後の "(" など) を入力すると、詳しい情報が表示されます。これを自動的に表示させたくない場合は、[オプション] ダイアログ ボックスの [エディタ] タブにある[ステートメント入力候補のオプション] のチェック ボックスをオフにしてください。以下では IntelliSence のさまざまなオプションについて機能と使用例を具体的に説明します。

メンバ自動表示
クラスまたは構造体の名前を入力した後に、'.' または '->' を入力すると、Visual C++ はメンバ変数とメンバ関数をアルファベット順に並べたリストを表示します。この候補リストから、マウスを使うか、目的の項目の先頭の文字を入力して、項目を選択することができます。

コード コメント
変数/メンバのリストを表示するとき、Visual C++ はそのクラスが定義されているヘッダーの中で、その項目に関するコメントを探します (まず関数定義の後ろを探し、そこになければ関数定義の前を探します)。コメントが見つかったら、パラメータ リストの隣のツールヒント ウィンドウに表示します。C と C++ の標準のコメント デリミタがサポートされています。

パラメータ ヒント
関数を選択し、'('という文字を入力すると、Visual C++ はその関数のパラメータをツールヒント ウィンドウに表示します。引数を入力していくにつれ、ツールヒント ウィンドウは現在のパラメータを太字で表示します。オーバーロードされている関数も、ツールヒントの中で、個々のインプリメンテーションをクリックすることで切り替えることができます。

型情報ヒント
型情報ヒントは、ファイルの編集中に、変数の型を表示します。変数の上にポインタを置いてしばらく待つと、ツールヒントに変数の型が表示されます。

グローバルな補完
グローバルな補完により、開発者はシステム アプリケーション プログラミング インターフェイス (API) 関数、使用可能なC++ クラス、インスタンス変数、およびローカル変数から選択を行うことができます。該当する関数や変数が存在するかどうかを自動検出することはできないので、関数/クラス リストを表示させるためには+を押す必要があります。

ClassView の動的な更新
Visual C++ 6.0 の IntelliSense を実現しているテクノロジを使用し、ClassView の動的な更新をサポートします。ClassView の動的な更新は、プロジェクトの保存、リビルド、またはコンパイルを行わなくても、ClassView のクラス、関数、および変数に関する情報を最新の状態に保つ機能です。たとえば、変数宣言を入力するときには、型の名前と、変数名の最初の文字が入力された時点で、その変数が ClassView に表示されます。

クイック マクロの記録
クイック マクロの記録は、キーストロークの記録と再生に似ていますが、それよりもさらに細かいマクロ記録機能を提供するメニュー ショートカットです。この [ツール] メニューからアクセスできる機能を使うと、Developer Studio のオブジェクト モデルがサポートしている任意のアクションを、一時的な VBScript マクロに記録することができます。

↑ 戻る

コンパイラの変更点

新規および改善された警告
一般的なプログラミング エラーを捕捉するために、新規のコンパイラ警告が追加されています。

未参照変数の検出機能の改善
未参照の変数は、デフォルト ビルド (/Od) と最適化ビルド (/Ox) の両方で検出されるようになりました。また、コンパイラは、ローカル変数が初期化されたが、参照されていないというようなケースも検出するようになりました。


int test()
{
   int   nValue = 15;
      // nValue not used in code...
}
さらに、これらのエラーは、関数の終わりではなく、シンボルを宣言している行の箇所で報告されます。

空のifステートメント
コンパイラは次のようなケースを捕捉します。


if ();      // note extra semi-colon
   ;   // should execute only if  is true
この際には、次の警告を発します。
4390 : ';' : 制御が空の文が見つかりました。意図した記述でしょうか?

記憶クラスまたは型指定子の検証の改善
コンパイラは、記憶クラス指定子が誤って置かれているさまざまな状況を検出します。次に例を示します。


__declspec(dllexport) extern "C" void foo();   // Here

class C {
   int * virtual vfunc();         // And here
};

この場合、コンパイラは次の警告を生成します。
4518 : 'specifier' : 予期されないストレージ クラスまたは型指定子です。無視されます

不正な、または存在しない代入
式ステートメントに、式の左辺に対して何の副作用もない演算子が含まれているようなケースが検出されるようになりました。


int i;
i == func();   // user intended i = func();
      // new warning C4553: '==' : 演算子にプログラム上の作用がありません。
      // '=' を意図しましたか?

または:


int i, j;
i + j;      // user intended ???
      // new warning 4552: 演算子にプログラム上の作用がありません。
      // 作用を持つ演算子を使用してください

<<の優先順位に関する誤りが含まれている可能性のあるケースに対する警告 演算子の優先順位に関する誤りが含まれている可能性のあるケースを検出します。


a = a << b + c;   // user probably intended (a << b) + c,
         // but will get a << (b + c)

この場合には、次のエラーが生成されます。
4554 : 'operator' : 演算子の優先順位に問題があります。カッコを使用して優先順位を明確にしてください

再定義に関する警告の改善
これまでのバージョンの Visual C++ では、再定義の警告を引き起こしたオリジナルの定義を探し出すのが難しいことがありました。Visual C++ 6.0 では、コンパイラは両方の宣言の位置を報告します。


int i;

char i;

この場合には、次のメッセージが生成されます。
test.cpp(3) : error C2371: 'i' :再定義されています。異なる基本型です。 test.cpp(1) : 'i' の宣言を確認してください。

テンプレートに関する警告の改善
これまでのバージョンの Visual C++ では、テンプレートを使用しているコードに関するエラー メッセージを理解するのが難しいことがありました。Visual C++ 6.0 は、理解しやすいように詳しい情報を提供します。次に例を示します。


template  class foo {
   T aT;          // Can't specialize on void
};

template  class foo2 {
   foo myFoo;       // This is OK, as long as foo is OK.
};

void bar()
{
   foo2 bad;
}

Visual C++ 5.0 では、次に示す最初のエラー メッセージだけが表示されていました。VisualC++ 6.0 では、関連するインスタンス生成の位置を示す情報が追加されています。
test.cpp(6) : error C2182: 'aT' : 'void' 型を使って宣言されました。
test.cpp(10) : コンパイルされたクラスのテンプレートのインスタンス化 'foo' の参照を確認してください
test.cpp(15): コンパイルされたクラスのテンプレートのインスタンス化 'foo2' の参照を確認してください

インラインの制御の改善
inline および __inline キーワードは、関数のインライン展開を試みるように指示するヒントであり、オプティマイザは最適化スイッチ (サイズと速度のどちらを最適化するか) とその他のヒューリスティックスに基づいて、インライン展開を行うかどうかを決定します。

オプティマイザの決定をオーバーライドして、つねにインライン展開を行うように指示する __forceinline キーワードが追加されました。ただし、コンパイラはコマンド ライン スイッチやその他の理由から、インライン展開を行わないことがあります。たとえば、オプティマイザは以下のケースではインライン展開を行いません。

  • デバッグ ビルドのデフォルトである /Ob0 が指定されている場合。
  • 関数呼び出しの中でのSEH/EHのミスマッチ (SEH が EH を、または EH が SEH を呼び出 している)。この場合、呼び出された側をインライン展開することはできません。
  • 可変長引数リストを取る関数。
  • 関数の再帰。この動作は、#pragma(inline_recursion, on) を指定することで、再帰のケースでもインライン展開を行うように変更することができます (ただし下記の制約があります)。
  • インラインの深さが上限を超えた場合。これは再帰のケースに適用されます。この値のデフォルト値は 8 で、#pragma(inline_depth, ) (n は 256 未満の数値) と #pragmainline_recursion(on | off) で制御することができます。
  • コピー構築されたオブジェクトを値で受け取る関数を、-GX/EHs/EHa でコンパイルした場合。
  • アンワインド可能オブジェクトを返す関数を、-GX/EHs/EHa でコンパイルした場合。
  • インライン アセンブラを使用する関数を、-Og/Ox/O1/O2 でコンパイルしなかった場合。 __forceinline が指定されたのに、関数のインライン展開を行えなかった場合、コンパイラはレベル 1 警告 (4714) を生成します。 このキーワードを使うときには、コードの動作と、コード サイズとコードの速度の間のトレードオフを理解し、結果を測定することが重要です。コードが大型化し、実行速度が低下する可能性があります。
    注: __forceinline が正常に機能しているかどうかは、必ずしもマップ ファイルからは判断できません。これは、関数のアドレスを取り出したり、関数をエクスポートしたりすると、関数のインスタンスが生成されるからです(この場合でも、関数が名前で呼び出されたときにはインライン展開が行われています)。
    関数のアドレスを取る式が存在すると、OBJ の中に関数のインスタンスが強制的に作られます。関数が関数ポインタを通して呼び出されると、OBJ の中の関数のインスタンスが呼び出されます。ただし、関数が名前で呼び出されたときには、(他にも制約がありますが) 関数のインライン展開が行われます。
    これは意図的な設計です。インライン展開はコンパイル時の操作であり、関数ポインタを通しての関数呼び出しは実行時の操作です。きわめて単純な例(たとえば、コンパイラが「ポインタを通しての関数呼び出しのアドレス」を最適化して関数に渡せる場合) を除き、関数ポインタを通しての関数呼び出しはインライン展開されませんし、その試みも行われません。

__assume キーワード
__assume キーワードは、引数として渡された式が、式が (他の変数との干渉や、何らかの変数への代入などによって) kill されるまで真であるということをオプティマイザに知らせるヒントです。デバッグ ビルドの際には、これを assert() と組み合わせて、__assume で使用している式がつねに真であることを確認することができます。
このキーワードのフォーマットを次に示します。


__assume(expr)

__assumeは一般にassertマクロの中で使用します。次に例を示します。


#ifdef DEBUG
# define ASSERT(e)    (void) ((e) ? 0 : assert(__FILE__, __LINE__))
# define ASSERTNR(e)    (void) ((e) ? 0 : assert(__FILE__, __LINE__))
#else // RELEASE build
# define ASSERT(e)    (void) (((e) ? 0 : assert(__FILE__, __LINE__)), __assume(e))
# define ASSERTNR(e)    (__assume(e))
#endif

#define UNREACHABLE   0

void foo(int *p)
{
      // Optimizer can eliminate p==NULL checks which might follow
   ASSERTNR(p!=NULL);

   switch(bar()){
      case 1:
         blah(1);
         break;

      case 2:
         blah(-1);
         break;

      default:
            // default case unreachable, so the optimizer
            // can assume bar() only returns 1 or 2, and
            // eliminate range checks
         ASSERTNR(UNREACHABLE);
   }
}

ランタイムのエラー チェック
Visual C++ 6.0 コンパイラは、一般的なバグを検出するいくつかのランタイム チェックを埋め込むことができます。これらのチェックは、-GZ コンパイラ フラグによって有効となります。ランタイム チェックはデバッグ (-Od) ビルドでしか使用できません。
以下のチェックが追加されています。

ローカル変数の自動初期化
すべてのローカル変数が特定の値 (0xCC) に初期化されます。これにより、初期化されていない変数が使用されているケースをキャッチすることができます。

関数ポインタ コール スタックの正当性チェック
スタック ポインタ (ESP) をチェックして、関数ポインタを通じた呼び出しの前後で変化がないかどうかを確認します。これにより、関数が __stdcall、__fastcall、または__thiscalL の呼び出し規約を使っている場合に、関数プロトタイプのパラメータの数が間違っているようなケースをキャッチすることができます。ただし、__cdecL の場合は、呼び出し側がパラメータのプッシュとポップを同時に行うため、キャッチされません。
また、__stdcall 関数が __cdecl 関数ポインタを通して呼び出されているケースもキャッチします。

コール スタックの正当性チェック
関数の終わりにスタック ポインタ (ESP) をチェックし、スタック ポインタが変更されていないことを確認します。これにより、ESP がインライン アセンブラの中で破壊されているケースや、関数の呼び出し規約が誤って宣言されているケースをキャッチすることができます。次に例を示します。


void foo(int i);    // misdefinition - should be void __stdcall foo(int
i);

void bar() {
...
foo(4);
...
// the ESP corruption caused by foo will be caught here, as bar exits
}

この例では、foo() のプロトタイプが間違っていることが検出されます。

operator delete の placement 形式
operator delete の placement 形式が新たにサポートされました。これにより、placement new が使用されている場合に、正しい delete 関数が呼び出されるようになります。

↑ 戻る

リンカの変更点

インポート ライブラリの形式の変更
インポート ライブラリの形式が、ライブラリ ファイルのサイズを小さくするように変更されました。 ほとんどのファイルが 50〜75% も小さくなっています。

遅延ロードインポート
リンカは、ダイナミック リンク ライブラリ (DLL) の遅延ロードを自動的に管理するためのコードを作成できるようになりました。これは、アプリケーションが DLL にリンクしているが、つねに使用するわけではないようなケースできわめて有効です。リンカ コマンドに次のスイッチを追加するだけで、遅延ロードが有効となります。


   /DELAYLOAD:

マップ ファイルのエクスポートの表示
エクスポートのリストを得るには、リンカのコマンド ラインに次のスイッチを追加します。


   /MAPINFO:EXPORTS

↑ 戻る

デバッガの変更点

エディット コンティニュー
エディット コンティニューにより、デバッガから出ずにコードを変更して実行を続けることができるため、生産性が大幅に改善されます。リビルドが必要な変更もありますが、ほとんどのケースではコードを変更してもそのままデバッグを続けることができます。

エディット コンティニューを有効にする方法

    注: エディット コンティニューは、Visual C++ 6.0 で作成されたデバッグ プロジェクトでは、デフォルトで有効となっています。しかし、以前のバージョンの Visual C++ からプロジェクトをインポートした場合には、以下の操作を行う必要があります。
    エディット コンティニューを有効にするには、次の設定を変更する必要があります。
  1. [ツール] メニューの [オプション] をクリックし、[デバッグ] タブの [デバッグ命令でエディット コンティニューを実行]チェック ボックスをオンにします。
  2. [プロジェクト] メニューの [設定] をクリックし、[C/C++] タブの [デバッグ情報]で[エディット コンティニュー用のプログラム データベース] を選択します。
  3. [プロジェクト] メニューの [設定] をクリックし、[リンク] タブの [カテゴリ]で[カスタマイズ] を選択し、[インクリメンタル リンクを行う] を選択します。
  4. さらに、プロジェクトを完全にリビルドする必要があります。

エディット コンティニューの使用
エディット コンティニューは、ほとんどの点でユーザーに対しては透過的に実行されます。編集した後にステップまたは実行を選択するだけで、エディット コンティニューが開始されます。コード編集がサポートされていない場合には、通常と同じようにリビルドを行うように求めるプロンプトが表示されます。

制約 エディット コンティニューがサポートしていない操作の例を示します。

  • ヘッダー ファイルの編集
  • C++ クラス定義の編集
  • 関数プロトタイプの編集
  • グローバル/静的コードの編集の大部分

バリアントとGUIDの表示のためのデコード

バリアントの表示
バリアントは自動的に正しい形式で表示されます。たとえば、整数は数値として、BSTR は文字列として表示されます。また、バリアントの型も表示されます。次に例を示します。


VARIANT vt;
vt.vt = VT_I2;
vt.iVal = 123

この例では、vt はローカル、ウォッチ、およびデータヒントのウィンドウに{123 VT_I2}と表示されます。VT_BYREF オブジェクトも、Microsoft Foundation Class(MFC) のCOleVariant と同様に、自動的に表示されます。

GUID とIIDの表示
IID、CLSID、およびREFIID を含むグローバル ユニーク識別子 (GUID) は、レジストリ内に名前が見つかれば名前によって、またそれに失敗した場合には通常の GUID の 16 進形式で表示されます。

スレッド情報ブロック
カレント スレッドのスレッド情報ブロック (TIB。TEB と間違って呼ばれることもあります) を表示する新しい擬似レジスタが追加されています。
普通のレジスタと同様に、@記号を前に付けることができます。TIBの内容に関する情報は、"Under the Hood"、Microsoft Systems Journal(MSJ), May 1996 にあります。

レジスタとして表示されるGetLastError()値
カレント スレッドの最後のエラー コードを表示する、ERRという名前の新しい擬似レジスタが追加されています。これは、GetLastError API を呼び出したときと同じ値を取得します。これを",hr"修飾子と組み合わせて使うと、非常に便利な出力が得られます。普通のレジスタと同様に、@記号を前に付けることができます。

MMX レジスタの表示
シンボル MM0〜MM7 を使って、MMX レジスタをウォッチ ウィンドウとクイックウォッチ ウィンドウに表示できるようになりました。MMX レジスタは64 ビット整数のレジスタで、MMX 命令をサポートするかどうかにかかわらず、すべてのx86マシン上で表示されます。MMX は IntelCorporation の登録商標です。

Vテーブルと関数ポインタ表示の改善
関数へのポインタとvテーブル エントリは、Visual C++ 5.0 のように単なる16進アドレスではなく、パラメータを含めて、可能な限りシンボルとして表示されます。

逆アセンブラ出力の改善
逆アセンブラの出力が、特に出力におけるシンボルの使用の面で改善されています。

アンデコレートされるシンボル
逆アセンブラとコールスタック ウィンドウは、DBG ファイルのシステム コールスタックを表示するときなどに、以前とは違ってC++名をアンデコレートするようになりました。たとえば、rpcrt4.DLL の中のある関数は、これまでは次のように意味不明の表示になっていました。


?MTAInvoke@@YGJPAUtagRPCOLEMESSAGE@@KPAUIRpcStubBuffer@@PAUIRpcChanne
lBuffer@@PAK@Z

現在では、正しい引数リストとともに、次のように表示されます

MTAInvoke

新しい書式指定記号
次の表は、Visual C++ 6.0 のウォッチ変数で使える新しい記号を示しています。
,hr 32 ビット値 (HRESULT または Win32 エラー コード) を、よりわかりやすい形で表示します。たとえば S_OK や E_NOTIMPL など、または "File not found" などのシステム エラー メッセージなどです。残念ながら、autoexp.dat の中で自動的な規則を設定することはできません。HRESULT などの型に対してこれを行うためには、ウォッチ ウィンドウでシンボル名に ",hr" のポストフィックスを手動で追加する必要があります。
,st Unicode 文字列の設定に応じて、文字列値を ANSI または Unicode として表示します。これはデフォルトの autoexp.dat ファイルで、現在の設定に従って MFC のCStringを表示するために使用されています。
,mq 既存の mb、mw、および md の書式指定記号と同様に、メモリをクワッドワードとして表示します。
,wm 数値をウィンドウ メッセージ番号 (WM_定数) にデコードして表示します。たとえば、次のコード
WORD wMessage = WM_CLOSE
は、wMessage,wm を {WM_CLOSE} と表示します。
,wc 数値をウィンドウ クラス フラグ (WC_定数) にデコードして表示します。

[COFF とエクスポートのロード] オプション
この新しいオプションは追加のデバッギング情報をロードします。このオプションは [オプション] ダイアログ ボックスの [デバッグ] タブにあります。これはデバッグ対象のコードのロード時のパフォーマンスに影響を与える可能性があるので、デフォルトではオフになっています。

COFF シンボル
他のデバッガは COFF シンボルを使用しますが、Visual C++ は使用しません (代わりに CodeView フォーマットのデバッグ情報を使用します)。しかし、他に情報がないときには、COFF 情報を読めると便利なことがあります。たとえば、Microsoft Visual Basic 5.01 開発システムにはデバッグ情報を COFF フォーマットで提供する msvbvm50.dbg ファイルがありますが、このオプションをオンにしておけば、このファイルを読み込むことができます。.dbgファイルにどのような形式のデバッグ情報が含まれているかを調べるには、dumpbin -headers を使用し、DebugDirectory で COFF を探してください。

エクスポート
他にデバッギング情報がない場合(たとえば Microsoft Windows 95 オペレーティング システムファイルなど)、このオプションはロードされている個々の DLL のエクスポート テーブルをシンボルに変換します。特定の DLLで、このオプションがどのような種類のシンボルを追加するかを調べるには、dumpbin -exports を使用します。

モジュール リスト
アプリケーションがシステム DLL や他の人のコードの呼び出し中にクラッシュしたとき、クラッシュの発生時にどの DLL がアクティブになっていたかを知るにはどうすればいいでしょうか? VisualC++ 6.0 の新しいモジュール リストは、プログラムが使用したすべての DLL のアドレス、名前、パス、およびロード順序を表示します。このダイアログ ボックスを使って、アプリケーションがクラッシュしたときにどの DLL がアクティブになっていたかを追跡することができます。

結論
Visual C++ 6.0 では、エディタ、コンパイラ、リンカ、およびデバッガに大きな変更が加えられていますが、Visual C++ の以前のバージョンからのプロジェクトの移行はスムーズに行うことができます。しかし、Visual C++ 6.0 の新しい機能をフルに活用するためには、どのような新機能があるのか、既存のコードにどのような影響があるのか、そしてどのように使えばいいのかを理解する必要があります。このドキュメントでは、これらの情報を提供しようと試みましたが、実際に理解するための一番の近道は、Visual C++ 6.0 をインストールし、サンプルをビルドし、コードをインポートすることです。このドキュメントと MSDN ライブラリを手がかりに、Visual C++ 6.0 をフルに活用してください。

↑ 戻る

(C) 1999 Microsoft Corporation. All rights reserved. Terms of Use.