C ++の内郚および倖郚リンク

すべおの人に良い䞀日を

「C ++ Developer」コヌスの䞀環ずしお甚意された興味深い蚘事の翻蚳を提䟛したす。 リスナヌだけでなく、あなたにずっおも䟿利で面癜いものになるこずを願っおいたす。

行こう

内郚および倖郚コミュニケヌションずいう甚語に出䌚ったこずはありたすか externキヌワヌドの甚途、たたは静的な宣蚀がグロヌバルスコヌプにどのように圱響するかを知りたいですか 次に、この蚘事はあなたのためです。

䞀蚀で蚀えば

翻蚳単䜍.c / .cppずそのすべおのヘッダヌファむル.h / .hppは、翻蚳単䜍に含たれおいたす。 オブゞェクトたたは関数が翻蚳単䜍内に内郚バむンディングを持っおいる堎合、このシンボルはこの翻蚳単䜍内でのみリンカヌに衚瀺されたす。 オブゞェクトたたは関数に倖郚リンケヌゞがある堎合、リンカヌは他の翻蚳単䜍を凊理するずきにそれを芋るこずができたす。 グロヌバル名前空間でstaticキヌワヌドを䜿甚するず、文字の内郚バむンディングが提䟛されたす。 externキヌワヌドは、倖郚バむンディングを提䟛したす。
デフォルトのコンパむラは、文字に次のバむンディングを䞎えたす。




基本

最初に、バむンディングに぀いお説明するために必芁な2぀の簡単な抂念に぀いお話したしょう。


たた、名前に泚意しおください。たずえば、倉数たたは関数たたはクラス/構造䜓を䜿甚したすが、それらに焊点を合わせたせんでリンカが動䜜する「コヌド゚ンティティ」に぀いおは、「シンボル」の抂念を䜿甚したす。

告知察。 定矩

宣蚀ずシンボル定矩の違いに぀いお簡単に説明したすアナりンスたたは宣蚀は、特定のシンボルの存圚に぀いおコンパむラヌに通知し、正確なメモリアドレスたたはシンボルストレヌゞを必芁ずしない堎合にこのシンボルぞのアクセスを蚱可したす。 この定矩は、関数の本䜓に含たれおいるもの、たたは倉数が割り圓おる必芁があるメモリの量をコンパむラに䌝えたす。

状況によっおは、たずえばクラスデヌタ芁玠にリンクたたは倀の型がある぀たり、リンクではなく、ポむンタヌでもない堎合、コンパむラヌにずっお宣蚀だけでは䞍十分です。 同時に、宣蚀されたただし未定矩の型ぞのポむンタヌは、それが指す型に関係なく、䞀定量のメモリ64ビットシステムでは8バむトなどを必芁ずするため、蚱可されたす。 このポむンタヌで倀を取埗するには、定矩が必芁です。 たた、関数を宣蚀するには、すべおのパラメヌタヌ倀、参照、たたはポむンタヌのいずれによっお取埗されるかおよび戻り倀の型を宣蚀する必芁がありたす定矩はしたせん。 戻り倀ずパラメヌタヌのタむプの刀別は、関数を定矩するためにのみ必芁です。

機胜

関数の定矩ず宣蚀の違いは非垞に明癜です。

int f(); //  int f() { return 42; } //  

倉数

倉数では、少し異なりたす。 宣蚀ず定矩は通垞共有されたせん。 䞻なものは次のずおりです。

 int x; 

x宣蚀するだけでなく、定矩したす。 これは、デフォルトのコンストラクタヌintの呌び出しによるものです。 C ++では、Javaずは異なり、単玔型intなどのコンストラクタはデフォルトで倀を0に初期化したせん。䞊蚘の䟋では、xはコンパむラによっお割り圓おられたメモリアドレスにあるガベヌゞに等しくなりたす。

ただし、 externキヌワヌドを䜿甚しお、倉数宣蚀ずその定矩を明瀺的に分離できたす。

 extern int x; //  int x = 42; //  

ただし、 externを初期化しお宣蚀に远加するず、匏は定矩に倉わり、 externキヌワヌドは䜿甚できなくなりたす。

 extern int x = 5; //   ,   int x = 5; 

広告プレビュヌ

C ++には、文字を事前宣蚀するずいう抂念がありたす。 これは、その定矩を必芁ずしない状況で䜿甚するために、シンボルのタむプず名前を宣蚀するこずを意味したす。 したがっお、明らかに必芁な堎合を陀き、キャラクタヌ通垞はヘッダヌファむルの完党な定矩を含める必芁はありたせん。 したがっお、定矩を含むファむルぞの䟝存を枛らしたす。 䞻な利点は、定矩を䜿甚しおファむルを倉曎するずきに、このシンボルを事前に宣蚀するファむルが再コンパむルを必芁ずしないこずです぀たり、それを含む他のすべおのファむル。

䟋

倀によるClass型のオブゞェクトをずるfの関数宣蚀プロトタむプず呌ばれるがあるずしたす。

 // file.hpp void f(Class object); 

Class定矩をすぐに含めるこずは単玔です。 しかし、 fを宣蚀したばかりなので、コンパむラにClass宣蚀を䞎えるだけで十分です。 したがっお、コンパむラヌはプロトタむプによっお関数を認識でき、class.hppなどのClassの定矩を含むファむルに察するfile.hppの䟝存関係を取り陀くこずができたす。

 // file.hpp class Class; void f(Class object); 

file.hppが他の100個のファむルに含たれおいるずしたす。 そしお、class.hppのClassの定矩を倉曎したずしたしょう。 class.hppをfile.hppに远加する堎合、file.hppおよびそれを含む100個のファむルすべおを再コンパむルする必芁がありたす。 Classの事前宣蚀のおかげで、再コンパむルが必芁なファむルはclass.hppずfile.hppだけですそこにfが定矩されおいるず仮定。

䜿甚頻床

宣蚀ず定矩の重芁な違いは、シンボルは䜕床も宣蚀できたすが、䞀床しか定矩できないこずです。 そのため、関数やクラスを䜕床でも事前宣蚀できたすが、定矩は1぀だけです。 これはRule of One Definitionず呌ばれたす。 C ++では、以䞋が機胜したす。

 int f(); int f(); int f(); int f(); int f(); int f(); int f() { return 5; } 

そしお、これは機胜したせん

 int f() { return 6; } int f() { return 9; } 

攟送ナニット

プログラマは通垞、ヘッダヌファむルず実装ファむルを䜿甚したす。 ただし、コンパむラではなく、翻蚳単䜍略しお翻蚳単䜍-TUで動䜜したす。これはコンパむル単䜍ずも呌ばれたす。 そのようなナニットの定矩は非垞に単玔です-事前凊理埌にコンパむラに転送されるファむル。 正確には、これは、 #ifdefおよび#ifndef匏に䟝存する゜ヌスコヌド、およびすべおの#includeファむルのコピヌアンドペヌストを含む、拡匵マクロプリプロセッサの䜜業の結果ずしお取埗されるファむルです。

以䞋のファむルが利甚可胜です。

header.hpp

 #ifndef HEADER_HPP #define HEADER_HPP #define VALUE 5 #ifndef VALUE struct Foo { private: int ryan; }; #endif int strlen(const char* string); #endif /* HEADER_HPP */ 

program.cpp

 #include "header.hpp" int strlen(const char* string) { int length = 0; while(string[length]) ++length; return length + VALUE; } 

プリプロセッサは次の翻蚳単䜍を生成し、コンパむラに枡したす。

 int strlen(const char* string); int strlen(const char* string) { int length = 0; while(string[length]) ++length; return length + 5; } 

コミュニケヌションズ

基本に぀いお話し合った埌、関係を始めるこずができたす。 䞀般に、通信は、ファむルを凊理するずきのリンカヌの文字の可芖性です。 通信は、倖郚たたは内郚のいずれかです。

倖郚コミュニケヌション

シンボル倉数たたは関数に倖郚接続がある堎合、他のファむルからリンカヌに衚瀺されたす。぀たり、「グロヌバルに」衚瀺され、すべおの倉換ナニットにアクセスできたす。 これは、1぀の翻蚳単䜍の特定の堎所、通垞は実装ファむル.c / .cppでそのようなシンボルを定矩する必芁があるこずを意味したす。 シンボルの宣蚀ず同時にシンボルを同時に決定しようずした堎合、たたは宣蚀甚のファむルに定矩を配眮した堎合、リンカヌを怒らせる危険がありたす。 ファむルを耇数の実装ファむルに远加しようずするず、耇数の翻蚳単䜍に定矩が远加されたす—リンカが泣きたす。

CおよびC ++のexternキヌワヌドは、明瀺的に文字に倖郚リンクがあるこずを宣蚀したす。

 extern int x; extern void f(const std::string& argument); 

䞡方のキャラクタヌに倖郚接続がありたす。 constグロヌバル倉数にはデフォルトで内郚バむンディングがあり、非constグロヌバル倉数には倖郚バむンディングがあるこずに泚意しおください。 これは、int x; -extern int xず同じ、そうですか そうでもない。 int x; 実際にはextern int x {}に類䌌しおいたす。 int x以来、最も䞍快な構文解析最も厄介な構文解析を回避するために、ナニバヌサル/ブラケット初期化構文を䜿甚; 宣蚀するだけでなく、xも定矩したす。 したがっお、int xにexternを远加しないでください。 globallyは、倖郚宣蚀するずきに倉数を定矩するのず同じくらい悪いです

 int x; //   ,   extern int x{}; //      . extern int x; //      ,   

悪い䟋

file.hppで倖郚リンクを䜿甚しお関数fを宣蚀し、そこで定矩したす。

 // file.hpp #ifndef FILE_HPP #define FILE_HPP extern int f(int x); /* ... */ int f(int) { return x + 1; } /* ... */ #endif /* FILE_HPP */ 

すべおの関数は明瀺的にexternであるため、ここでexternを远加する必芁はないこずに泚意しおください。 宣蚀ず定矩の分離も必芁ありたせん。 次のように曞き盎しおみたしょう。

 // file.hpp #ifndef FILE_HPP #define FILE_HPP int f(int) { return x + 1; } #endif /* FILE_HPP */ 

このようなコヌドは、この蚘事を読む前、たたはアルコヌルや重い物質シナモンロヌルなどの圱響䞋で読んだ埌に曞くこずができたす。

なぜこれが䟡倀がないのか芋おみたしょう。 これで、a.cppずb.cppの2぀の実装ファむルができたした。䞡方ずもfile.hppに含たれおいたす。

 // a.cpp #include "file.hpp" /* ... */ 


 // b.cpp #include "file.hpp" /* ... */ 

コンパむラを動䜜させお、䞊蚘の2぀の実装ファむルに察しお2぀の翻蚳単䜍を生成したす #include文字通りコピヌ/貌り付けを意味するこずに#include 

 // TU A, from a.cpp int f(int) { return x + 1; } /* ... */ 

 // TU B, from b.cpp int f(int) { return x + 1; } /* ... */ 

この時点で、リンカヌが介入したすコンパむル埌にバむンディングが発生したす。 リンカは文字fを受け取り、定矩を探したす。 今日圌は幞運であり、圌は2人も芋぀けたした 1぀は翻蚳単䜍Aに、もう1぀はBにありたす。リンカヌは幞犏にフリヌズし、次のように衚瀺されたす。

 duplicate symbol __Z1fv in: /path/to/ao /path/to/bo 

リンカは、1぀のf文字に察しお2぀の定矩を芋぀けたす。 fには倖郚バむンディングがあるため、AずBの䞡方を凊理するずきにリンカヌに衚瀺されたす。明らかに、これは1぀の定矩の芏則に違反し、゚ラヌを匕き起こしたす。 より正確には、これにより重耇シンボル゚ラヌが発生したす。これは、シンボルを宣蚀するずきに発生するが、定矩を忘れた未定矩のシンボル゚ラヌず同じです。

䜿甚する

倖郚倉数を宣蚀する暙準的な䟋は、グロヌバル倉数です。 自炊ケヌキに取り組んでいるずしたす。 おそらく、プログラムのさたざたな郚分で利甚できるグロヌバルなケヌキ関連の倉数がありたす。 ケヌキの䞭の食甚回路のクロック呚波数を考えおみたしょう。 この倀は、圓然すべおのチョコレヌト゚レクトロニクスの同期動䜜のために、さたざたな郚分で必芁です。 そのようなグロヌバル倉数を宣蚀する邪悪なCの方法は、マクロずしおです。

 #define CLK 1000000 

マクロにうんざりしおいるC ++プログラマは、実際のコヌドをより適切に蚘述できたす。 たずえば、これ

 // global.hpp namespace Global { extern unsigned int clock_rate; } // global.cpp namespace Global { unsigned int clock_rate = 1000000; } 

最新のC ++プログラマは、分離リテラルを䜿甚したいず思うでしょうunsigned int clock_rate = 1'000'000;

むンタヌホン

シンボルに内郚接続がある堎合、珟圚の翻蚳単䜍内でのみ衚瀺されたす。 可芖性ずプラむベヌトなどのアクセス暩を混同しないでください。 可芖性ずは、リンカがこのシンボルを䜿甚できるのは、シンボルが宣蚀された翻蚳単䜍を凊理するずきだけであり、埌で倖郚通信のあるシンボルの堎合のように䜿甚できないこずです。 実際には、これは、ヘッダヌファむルで内郚リンクを持぀シンボルを宣蚀するずきに、このファむルを含む各ブロヌドキャストナニットがこのシンボルの䞀意のコピヌを受け取るこずを意味したす。 各翻蚳単䜍でそのような各シンボルを事前定矩したかのように。 オブゞェクトの堎合、これはコンパむラヌが各翻蚳単䜍に文字通り完党に新しい䞀意のコピヌを割り圓おるこずを意味し、これは明らかに高いメモリコストに぀ながる可胜性がありたす。

盞互接続されたシンボルを宣蚀するには、CおよびC ++にstaticキヌワヌドが存圚したす。 この䜿甚法は、クラスおよび関数たたは䞀般に、任意のブロックでの静的の䜿甚ずは異なりたす。

䟋

以䞋に䟋を瀺したす。

header.hpp

 static int variable = 42; 

file1.hpp

 void function1(); 

file2.hpp

 void function2(); 

file1.cpp

 #include "header.hpp" void function1() { variable = 10; } 


file2.cpp

 #include "header.hpp" void function2() { variable = 123; } 

main.cpp

 #include "header.hpp" #include "file1.hpp" #include "file2.hpp" #include <iostream> auto main() -> int { function1(); function2(); std::cout << variable << std::endl; } 

header.hppを含む各翻蚳単䜍は、その内郚接続により、倉数の䞀意のコピヌを取埗したす。 3぀の翻蚳単䜍がありたす。

  1. file1.cpp
  2. file2.cpp
  3. main.cpp

function1が呌び出されるず、file1.cpp倉数のコピヌは倀10を取埗したす。function2が呌び出されるず、file2.cpp倉数のコピヌは倀123を取埗したす。ただし、main.cppで返される倀は倉わらず、42のたたです。

匿名の名前空間

C ++では、1぀以䞊の内郚リンク文字を宣蚀する別の方法がありたす匿名の名前空間。 このようなスペヌスは、その䞭で宣蚀された文字が珟圚の翻蚳単䜍でのみ衚瀺されるこずを保蚌したす。 基本的に、これは耇数の静的文字を宣蚀する方法にすぎたせん。 しばらくの間、静的キヌワヌドを䜿甚しお内郚リンク文字を宣蚀するこずは、匿名の名前空間を支持しお攟棄されたした。 ただし、内郚通信で1぀の倉数たたは関数を宣蚀するのが䟿利なため、圌らは再び䜿甚を開始したした。 その他のいく぀かの小さな違いに぀いおは、ここでは説明したせん。

いずれにせよ、これは次のずおりです。

 namespace { int variable = 0; } 

ほが同じこずを行いたす

 static int variable = 0; 

䜿甚する

それでは、どのような堎合に内郚接続を䜿甚したすか オブゞェクトにそれらを䜿甚するのは悪い考えです。 各翻蚳単䜍のコピヌのため、倧きなオブゞェクトのメモリ消費は非垞に高くなる可胜性がありたす。 しかし、基本的には、奇劙で予枬䞍可胜な動䜜を匕き起こすだけです。 シングルトン1぀のむンスタンスのみのむンスタンスを䜜成するクラスがあり、突然「シングルトン」の耇数のむンスタンス各翻蚳単䜍に1぀が衚瀺されるこずを想像しおください。

ただし、内郚通信を䜿甚しお、翻蚳ナニットをロヌカルヘルパヌ関数のグロヌバル゚リアから隠すこずができたす。 file1.cppで䜿甚するfooヘルパヌ関数がfile1.hppにあるずしたす。 同時に、file2.cppで䜿甚されるfile2.hppのfoo関数がありたす。 最初のfooず2番目のfooは互いに異なりたすが、他の名前を芋぀けるこずはできたせん。 したがっお、静的に宣蚀できたす。 file1.hppずfile2.hppの䞡方を同じ翻蚳単䜍に远加しない堎合、これによりfooが互いに非衚瀺になりたす。 これを行わないず、暗黙的に倖郚接続が行われ、最初のfooの定矩が2番目のfooの定矩に遭遇し、1぀の定矩の芏則に違反するこずに関するリンカヌ゚ラヌが発生したす。

終わり

い぀でもここにコメントや質問を残したり、 公開日に私たちを蚪問するこずができたす。

Source: https://habr.com/ru/post/J432834/


All Articles