Как реализовать одно правило определения - PullRequest
2 голосов
/ 19 декабря 2008

В этом сообщении ссылка на Правило Одного Определения.

Википедия довольно плохо объясняет, как ее реализовать

Где я могу найти хорошие источники о руководящих принципах в C ++ .NET?

Ответы [ 2 ]

6 голосов
/ 19 декабря 2008

Одно правило определения в основном означает, что переменная / функция может быть расположена только в одном месте в адресном пространстве скомпилированного исполняемого файла. Один из способов думать об этом - во время компиляции есть массив памяти, который будет использоваться в скомпилированной программе (объектный код), и таблица поиска для ссылки на местоположения переменных / функций. Это делается на уровне процесса. Предположим, что следующая простая программа:

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 будет заменен адресом некоторой переменной.

Файл2 заканчивается примерно так:

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. Аналогично, если есть определение no , то вы получите ужасные ошибки компоновщика.

«Решением» правила является разбиение вашего файла таким образом, чтобы у вас были определения только в файлах .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, поскольку отсутствует концепция различия между объявлением и определением.

1 голос
/ 19 декабря 2008

Самый простой способ выполнить одно правило определения - это поместить определение в файл .cpp вместо заголовка.

Люди иногда помещают определения в заголовки, используя макросы и / или условные выражения препроцессора, чтобы объект или функция были определены только в одном модуле компиляции. Но обычно проще (и, конечно, более понятно) просто поместить определение в файл .cpp.

...