C++何年も勉強してきたのに,君はまだ知らないことだ.
9837 ワード
テキストリンク:http://www.cnblogs.com/MrYuan/p/5033055.html
C++はここ数年、クラスを定義する理由を知っています.クラスの定義は.hファイルに、クラスの実装はcppファイルにあります.なぜ関連付けられるのでしょうか?ファイルの中に何が置けるか知っていますか.できません.何かをcppファイルに入れることができます.もしあなたが忘れたり、根こそぎ分からなかったりしたら、この文を読んだことがあるとはっきりします!!
宣言と定義
宣言は、プログラムに名前を導入することです.定義は、プログラム内のエンティティの唯一の説明を提供します.宣言と定義は同時に存在する場合があります.
例えばint a;
extern int b=1;
宣言は、externに初期化式が存在しない場合にのみ宣言されます.その他の状況は定義であり宣言でもある.
ただし、次の場合、宣言は宣言にすぎません.
1:関数プロトタイプのみを提供します.voidのように func(int,int);
2: extern int a;
3:class A;
4:typedef宣言
5:クラスで定義された静的データ・メンバーの宣言
次のようになります.
次の場合 ,定義は定義のみです.
1:クラス定義以外の静的データ・メンバーを定義して初期化します.のように A::a=0;
2:非インラインメンバー関数をクラス外で定義します.
宣言は、1つのシンボルを1つの役割ドメインに導入するだけです.定義は、プログラム内のエンティティの唯一の説明を提供します.指定した定義ドメインでシンボルを繰り返し宣言することはできますが、定義を繰り返すことはできません.そうしないと、コンパイルエラーが発生します.しかし、クラス内のメンバー関数と静的データメンバーは例外であり、クラス内では宣言されていますが、複数は存在しません.
次のようになります.
宣言と定義の違いがわかり、内部リンク、外部リンクも理解する必要があります.それらを理解してこそ、最初に提起された問題を知ることができます.
コンパイル時、コンパイラはプログラム構文と関数、変数が宣言されているかどうかを検出します.関数が宣言されていない場合、コンパイラは警告を与えますが、ターゲットファイルを生成できます.リンクプログラムの場合、リンクはすべてのターゲットファイルで関数の実装を探します.見つからない場合はリンクエラーコード(Linker)を報告します Error).VCでは、このエラーは一般的にLinkです. 2001エラーは、リンクが関数の実装を見つけられなかったことを意味します.
リンクは、異なるコンパイルユニットで生成されたシンボルを関連付けます.内部リンクと外部リンクの2つのリンク方法があります.
シンボル名がコンパイルユニットにとってローカルであり、リンク時に他のコンパイルユニットと同じ名前と競合することができない場合、このシンボルは内部リンクです.内部リンクは、このシンボルへのアクセスが現在のコンパイルユニットに限られ、他のコンパイルユニットには表示されないことを意味します.
staticキーワードがグローバル変数に作用する場合、静的グローバル変数を表します.ただし、スコープは現在のファイルスコープ内にのみ存在します.他のファイルではextern宣言を使用しても使用できません.constも似ています.
static、constキーワード、列挙タイプを持つ接続は内部です.
内部リンクを持つ記号は、現在のファイルの外部には使用できません.プログラムの他の部分に影響を与えるには、.hファイルに配置します.このとき、この.hファイルを含むすべてのソースファイルには独自の定義があり、互いに影響しません.
クラスの定義には内部リンクがあります.定義であるため、同じコンパイルユニットで重複することはできません.他のコンパイルユニットで使用する必要がある場合は、クラスをヘッダファイルに定義し、他のファイルに含める必要があります.他のファイルでのみclassを使用 a;クラスの定義は内部リンクであり、ターゲットファイルにシンボルがエクスポートされないため、宣言はできません.他のユニットによって未定義のシンボルが解析されません.この点を理解することが大切です.
インライン関数にも内部リンクがあります.
複数のファイルのプログラムで、1つのシンボルがリンク時に他のコンパイルユニットと対話できる場合、この名前には外部リンクがあります.外部リンクは、この定義が単一のコンパイルユニットに限定されないことを意味する..oファイルに外部シンボルを生成できます.未定義のシンボルを解析するために他のコンパイルユニットによってアクセスできます.したがって、プログラム全体で一意でなければなりません.そうしないと、定義が繰り返されます.
非インラインメンバー関数、非インライン関数、非静的自由関数には外部リンクがあります.
コンパイラは可能なときにすべての 関数の呼び出しは関数体に置き換えられ、.oファイルには記号は書き込まれません.
シンボルが内部リンクなのか外部リンクなのかを判断する良い方法は、シンボルが.oファイルに書き込まれているかどうかを見ることです.
前述したのはリンク方式への影響を定義し,次にリンク方式への影響を宣言する.
宣言は現在のコンパイルユニットにのみ役立つため、宣言は.oファイルに何も書き込まない.
例えばextern int a;
int func();
これらの宣言自体は.oファイルの内容には影響しません.それぞれは、現在のコンパイルユニットが必要に応じて対応するグローバル定義にアクセスできるように、外部シンボルに名前を付けるだけです.
関数呼び出しにより、定義されていない記号が.oファイルに書き込まれます.aがこのファイルで使用されていない場合、.oファイルには書き込まれません.func関数にはこの関数の呼び出しがあります.この記号はターゲットファイルにも書き込まれます.その後、この.oファイルは、この記号を定義する.oファイルと接続され、前に定義されていない記号が解析されます.
上記の宣言により、シンボルがターゲットファイルに書き込まれる可能性があります.ただし、次の宣言では、シンボルがターゲットファイルに書き込まれることはありません.
次のようになります.
リンクも内部にあります.
クラス宣言とクラス定義は内部リンクです.現在のコンパイルユニットにのみ使用されます.
静的クラス・データ・メンバーの定義には外部リンクがあります.のように
静的データメンバーaは宣言にすぎないが、その定義A::a=0;外部リンクがあります.
C++はクラスと列挙タイプに対する処理方式が異なる.たとえば、クラスを定義しないときにクラスを宣言できます.ただし、定義なしに列挙タイプを宣言することはできません.
以上の解析に基づいて,外部リンクを持つ定義をヘッダファイルに置くのはほとんどプログラミングエラーであることが分かる.ヘッダファイルに複数のソースファイルが含まれている場合、複数の定義が存在し、リンク時にエラーが発生するためです.
ヘッダファイルに内部リンクを配置する定義は合法ですが、推奨されません.ヘッダファイルが複数のソースファイルに含まれると、グローバルネーミングスペースが汚染されるだけでなく、各コンパイルユニットに独自のエンティティが存在するためです.メモリ容量を大量に消費し、マシンのパフォーマンスにも影響します.
constおよびstatic修飾のグローバル変数は、現在のファイルの役割ドメイン内でのみ有効です.内部リンク属性があります.
ヘッダファイルに書き込むべきか、書き込むべきでないかの定義を以下に示します.
なぜタイプでメンバー関数だけを宣言し、それを実現しないのが合法なのか、今分かっていると思います.クラスの定義を.hファイルに入れる理由にも答えることができます.クラスのインプリメンテーションは、同じ名前のcppファイルに格納できます.先生の以前の紹介では、コンパイラが同名のcppファイルを自動的に探しているということです.実はcppファイルにはメンバー関数の実装が格納されているため、メンバー関数には外部リンク特性があり、ターゲットファイルにシンボルが生成されます.このファイルでは、この記号は定義されています.このメンバー関数を呼び出す他のターゲットファイルにも、未定の記号が生成されます.2つのターゲットファイルが接続されると、この記号は解析されます.注意staticデータメンバーはcppファイルに格納する必要があります.ファイルに置くことはできません.
内部リンクの定義はcppファイルに定義でき、グローバルなシンボル空間には影響しません. .ただし、cppファイルの役割ドメインでは、外部リンクがあるため、定義(禁止されていない)が静的データと関数として宣言されていないことを避けます.
のように
上記の定義に外部リンクがあると、グローバルネーミングスペースの他のシンボル名と競合する可能性があります.グローバルな変数または関数を使用する必要がある場合.staticキーワードを追加できます.役割ドメインを現在のファイルに限定し、内部リンクを持つとグローバルネーミングスペースに影響を与えません.インライン関数と静的自由関数、列挙、constタイプのデータには内部リンクがあるため、グローバルネーミングスペースに影響を与えることなくcppファイルで定義できます.
typedefとマクロ定義は.oファイルに記号を導入しません.cppファイルにも表示され、グローバルネーミングスペースに影響を与えません.
typedef 既存のタイプに別名を作成します.新しいタイプを作成するのではなく、タイプセキュリティは提供されません.のように
Intaが必要な場所でIntBを使うのは間違いありません.互いに置き換えることができますこれにより、タイプセキュリティは提供されません.しかし、関数タイプを定義する際にtypedefがよく使用され、定義をより明確にすることができます.
標準cライブラリは、与えられた式の値がゼロでないことを保証するためにassertマクロを提供します.エラーメッセージが出力され、プログラムの実行が終了します.プログラムにNDEBUGが定義されていない場合にのみassertが動作します.NDEBUGが定義されると ,assert文は無視されます .注意VCのASSERTとは違います.ASSERTはvcで提供されています.とき_DEBUGが定義されている場合にのみ機能します.
vcのDEBUGモードで_DEBUGが定義されます.RELEASEモードではNDEBUGが定義されます.
転載先:https://www.cnblogs.com/MrYuan/p/5033055.html
C++はここ数年、クラスを定義する理由を知っています.クラスの定義は.hファイルに、クラスの実装はcppファイルにあります.なぜ関連付けられるのでしょうか?ファイルの中に何が置けるか知っていますか.できません.何かをcppファイルに入れることができます.もしあなたが忘れたり、根こそぎ分からなかったりしたら、この文を読んだことがあるとはっきりします!!
宣言と定義
宣言は、プログラムに名前を導入することです.定義は、プログラム内のエンティティの唯一の説明を提供します.宣言と定義は同時に存在する場合があります.
例えばint a;
extern int b=1;
宣言は、externに初期化式が存在しない場合にのみ宣言されます.その他の状況は定義であり宣言でもある.
ただし、次の場合、宣言は宣言にすぎません.
1:関数プロトタイプのみを提供します.voidのように func(int,int);
2: extern int a;
3:class A;
4:typedef宣言
5:クラスで定義された静的データ・メンバーの宣言
次のようになります.
class A
{
public:
static int a;// 。
};
次の場合 ,定義は定義のみです.
1:クラス定義以外の静的データ・メンバーを定義して初期化します.のように A::a=0;
2:非インラインメンバー関数をクラス外で定義します.
宣言は、1つのシンボルを1つの役割ドメインに導入するだけです.定義は、プログラム内のエンティティの唯一の説明を提供します.指定した定義ドメインでシンボルを繰り返し宣言することはできますが、定義を繰り返すことはできません.そうしないと、コンパイルエラーが発生します.しかし、クラス内のメンバー関数と静的データメンバーは例外であり、クラス内では宣言されていますが、複数は存在しません.
次のようになります.
class A
{
public:
static int a;
static int a;
void func(int ,int);
void func(int ,int);
};
宣言と定義の違いがわかり、内部リンク、外部リンクも理解する必要があります.それらを理解してこそ、最初に提起された問題を知ることができます.
コンパイル時、コンパイラはプログラム構文と関数、変数が宣言されているかどうかを検出します.関数が宣言されていない場合、コンパイラは警告を与えますが、ターゲットファイルを生成できます.リンクプログラムの場合、リンクはすべてのターゲットファイルで関数の実装を探します.見つからない場合はリンクエラーコード(Linker)を報告します Error).VCでは、このエラーは一般的にLinkです. 2001エラーは、リンクが関数の実装を見つけられなかったことを意味します.
リンクは、異なるコンパイルユニットで生成されたシンボルを関連付けます.内部リンクと外部リンクの2つのリンク方法があります.
シンボル名がコンパイルユニットにとってローカルであり、リンク時に他のコンパイルユニットと同じ名前と競合することができない場合、このシンボルは内部リンクです.内部リンクは、このシンボルへのアクセスが現在のコンパイルユニットに限られ、他のコンパイルユニットには表示されないことを意味します.
staticキーワードがグローバル変数に作用する場合、静的グローバル変数を表します.ただし、スコープは現在のファイルスコープ内にのみ存在します.他のファイルではextern宣言を使用しても使用できません.constも似ています.
static、constキーワード、列挙タイプを持つ接続は内部です.
内部リンクを持つ記号は、現在のファイルの外部には使用できません.プログラムの他の部分に影響を与えるには、.hファイルに配置します.このとき、この.hファイルを含むすべてのソースファイルには独自の定義があり、互いに影響しません.
クラスの定義には内部リンクがあります.定義であるため、同じコンパイルユニットで重複することはできません.他のコンパイルユニットで使用する必要がある場合は、クラスをヘッダファイルに定義し、他のファイルに含める必要があります.他のファイルでのみclassを使用 a;クラスの定義は内部リンクであり、ターゲットファイルにシンボルがエクスポートされないため、宣言はできません.他のユニットによって未定義のシンボルが解析されません.この点を理解することが大切です.
インライン関数にも内部リンクがあります.
複数のファイルのプログラムで、1つのシンボルがリンク時に他のコンパイルユニットと対話できる場合、この名前には外部リンクがあります.外部リンクは、この定義が単一のコンパイルユニットに限定されないことを意味する..oファイルに外部シンボルを生成できます.未定義のシンボルを解析するために他のコンパイルユニットによってアクセスできます.したがって、プログラム全体で一意でなければなりません.そうしないと、定義が繰り返されます.
非インラインメンバー関数、非インライン関数、非静的自由関数には外部リンクがあります.
コンパイラは可能なときにすべての 関数の呼び出しは関数体に置き換えられ、.oファイルには記号は書き込まれません.
シンボルが内部リンクなのか外部リンクなのかを判断する良い方法は、シンボルが.oファイルに書き込まれているかどうかを見ることです.
前述したのはリンク方式への影響を定義し,次にリンク方式への影響を宣言する.
宣言は現在のコンパイルユニットにのみ役立つため、宣言は.oファイルに何も書き込まない.
例えばextern int a;
int func();
これらの宣言自体は.oファイルの内容には影響しません.それぞれは、現在のコンパイルユニットが必要に応じて対応するグローバル定義にアクセスできるように、外部シンボルに名前を付けるだけです.
関数呼び出しにより、定義されていない記号が.oファイルに書き込まれます.aがこのファイルで使用されていない場合、.oファイルには書き込まれません.func関数にはこの関数の呼び出しがあります.この記号はターゲットファイルにも書き込まれます.その後、この.oファイルは、この記号を定義する.oファイルと接続され、前に定義されていない記号が解析されます.
上記の宣言により、シンボルがターゲットファイルに書き込まれる可能性があります.ただし、次の宣言では、シンボルがターゲットファイルに書き込まれることはありません.
次のようになります.
typedef int Int;Class A;
struct s;
union point;
リンクも内部にあります.
クラス宣言とクラス定義は内部リンクです.現在のコンパイルユニットにのみ使用されます.
静的クラス・データ・メンバーの定義には外部リンクがあります.のように
class A
{
static int a;// 。 。
};
静的データメンバーaは宣言にすぎないが、その定義A::a=0;外部リンクがあります.
C++はクラスと列挙タイプに対する処理方式が異なる.たとえば、クラスを定義しないときにクラスを宣言できます.ただし、定義なしに列挙タイプを宣言することはできません.
以上の解析に基づいて,外部リンクを持つ定義をヘッダファイルに置くのはほとんどプログラミングエラーであることが分かる.ヘッダファイルに複数のソースファイルが含まれている場合、複数の定義が存在し、リンク時にエラーが発生するためです.
ヘッダファイルに内部リンクを配置する定義は合法ですが、推奨されません.ヘッダファイルが複数のソースファイルに含まれると、グローバルネーミングスペースが汚染されるだけでなく、各コンパイルユニットに独自のエンティティが存在するためです.メモリ容量を大量に消費し、マシンのパフォーマンスにも影響します.
constおよびstatic修飾のグローバル変数は、現在のファイルの役割ドメイン内でのみ有効です.内部リンク属性があります.
ヘッダファイルに書き込むべきか、書き込むべきでないかの定義を以下に示します.
//test.h
#ifndef TEST_H
#define TEST_H
int a; //a , 。
extern int b=10;// 。
const int c=2;//c , 。
static int d=3;// 。
static void func(){} // 。
void func2(){} // a。
void func3();// 。 。 。
class A
{
public:
static int e;// , 。
int f;// , 。
void func4();// , 。 。
};
A::e=10;// 。 。
void A:func4()// , 。 。
{
//,......
}
#endif
なぜタイプでメンバー関数だけを宣言し、それを実現しないのが合法なのか、今分かっていると思います.クラスの定義を.hファイルに入れる理由にも答えることができます.クラスのインプリメンテーションは、同じ名前のcppファイルに格納できます.先生の以前の紹介では、コンパイラが同名のcppファイルを自動的に探しているということです.実はcppファイルにはメンバー関数の実装が格納されているため、メンバー関数には外部リンク特性があり、ターゲットファイルにシンボルが生成されます.このファイルでは、この記号は定義されています.このメンバー関数を呼び出す他のターゲットファイルにも、未定の記号が生成されます.2つのターゲットファイルが接続されると、この記号は解析されます.注意staticデータメンバーはcppファイルに格納する必要があります.ファイルに置くことはできません.
内部リンクの定義はcppファイルに定義でき、グローバルなシンボル空間には影響しません. .ただし、cppファイルの役割ドメインでは、外部リンクがあるため、定義(禁止されていない)が静的データと関数として宣言されていないことを避けます.
のように
int a;
void func()
{
//......
}
上記の定義に外部リンクがあると、グローバルネーミングスペースの他のシンボル名と競合する可能性があります.グローバルな変数または関数を使用する必要がある場合.staticキーワードを追加できます.役割ドメインを現在のファイルに限定し、内部リンクを持つとグローバルネーミングスペースに影響を与えません.インライン関数と静的自由関数、列挙、constタイプのデータには内部リンクがあるため、グローバルネーミングスペースに影響を与えることなくcppファイルで定義できます.
typedefとマクロ定義は.oファイルに記号を導入しません.cppファイルにも表示され、グローバルネーミングスペースに影響を与えません.
typedef 既存のタイプに別名を作成します.新しいタイプを作成するのではなく、タイプセキュリティは提供されません.のように
typedef int IntA;
typedef int IntB;
Intaが必要な場所でIntBを使うのは間違いありません.互いに置き換えることができますこれにより、タイプセキュリティは提供されません.しかし、関数タイプを定義する際にtypedefがよく使用され、定義をより明確にすることができます.
標準cライブラリは、与えられた式の値がゼロでないことを保証するためにassertマクロを提供します.エラーメッセージが出力され、プログラムの実行が終了します.プログラムにNDEBUGが定義されていない場合にのみassertが動作します.NDEBUGが定義されると ,assert文は無視されます .注意VCのASSERTとは違います.ASSERTはvcで提供されています.とき_DEBUGが定義されている場合にのみ機能します.
vcのDEBUGモードで_DEBUGが定義されます.RELEASEモードではNDEBUGが定義されます.
転載先:https://www.cnblogs.com/MrYuan/p/5033055.html