Этот пост ссылается на правило единого определения.
Википедия плохо объясняет, как это реализовать
Где я могу найти хорошие источники о рекомендациях, которым следует следовать в C ++ .NET?
Этот пост ссылается на правило единого определения.
Википедия плохо объясняет, как это реализовать
Где я могу найти хорошие источники о рекомендациях, которым следует следовать в C ++ .NET?
Одно правило определения в основном означает, что переменная / функция может быть расположена только в одном месте в адресном пространстве скомпилированного исполняемого файла. Один из способов подумать об этом - во время компиляции существует массив памяти, который будет использоваться в скомпилированной программе (объектный код), и таблица поиска для ссылки на местоположения переменных / функций. Это делается на уровне процесса. Предположим, что это простая программа:
file1.cpp
int square(int x); // this is a declaration
extern int someVariable; // this is a declration
void square(int x) // this is a definition
{
return x * someVariable;
}
file2.cpp
int square(int x); // this is a declaration
int someVariable; // this is a definition
void main()
{
someVariable = 12;
someVariable = square(4);
}
Когда компилятор начинает компилировать объектный код, он читает объявления и помещает их в свою таблицу. В конце компиляции file1.cpp получится что-то вроде этого:
declarations:
square (XX): function that returns int, and takes a single int as parameter [4 bytes]
someVariable (YY): integer [4 bytes]
data:
12 34 56 78 aa XX XX XX XX ab cd
definition:
square: starts at address 0
Это предполагает, что функция компилируется для этих конкретных инструкций по сборке. Во время компоновщика XX XX XX XX будет заменен адресом someVariable.
File2 заканчивается примерно так:
declarations:
square (XX): function that returns int, and takes a single int as parameter [4 bytes]
someVariable (YY): integer [4 bytes]
data:
00 00 00 00 12 34 56 78 12 34 56 YY YY YY YY 23 21
definitions:
someVariable: starts at address 0
main: starts at address 4
И в этом случае YY будет заменен адресом квадрата.
Вот где в игру вступает линкер. Задача компоновщика - просмотреть список и построить таблицу, в которой все находится в адресном пространстве программы во время компиляции. Однако возникает проблема, если два объектных файла имеют одно и то же определение переменной при попытке связи. Если бы в приведенном выше примере было два определения someVariable, то он не знал бы, чем заменить YY. Точно так же, если определения нет, вы получите ужасные ошибки компоновщика.
«Решение» этого правила состоит в том, чтобы разделить ваш файл таким образом, чтобы у вас были определения только в файлах .cpp и объявления вещей в ваших файлах .h, поэтому приведенный выше пример будет выглядеть следующим образом:
file1.cpp
#include "file2.h"
void square(int x) // this is a definition
{
return x * someVariable;
}
file1.h
int square(int x); // this is a declaration
file2.cpp
#include "file1.h"
int someVariable; // this is a definition
void main()
{
someVariable = 12;
someVariable = square(4);
}
file2.h
extern int someVariable;
Обратите внимание, что это невероятно простой пример, и что он на самом деле неприменим в .NET, поскольку нет концепции различия между объявлением и определением.
Самый простой способ выполнить одно правило определения - поместить определение в файл .cpp вместо заголовка.
Иногда люди помещают определения в заголовки, используя макросы и / или условные выражения препроцессора, чтобы объект или функция были определены только в одной единице компиляции. Но обычно проще (и, конечно, более понятно) просто поместить определение в файл .cpp.