Есть ли у классов внешняя связь? - PullRequest
9 голосов
/ 24 июня 2011

У меня есть 2 файла A.cpp и B.cpp, которые выглядят примерно так:

A.cpp
----------
class w
{
public:
    w();
};


B.cpp
-----------
class w
{
public:
    w();
};

Теперь я где-то читал (http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr082.htm), что классы имеют внешнюю связь. Поэтому при сборке я ожидалмножественная ошибка определения, но, наоборот, она работала как шарм. Однако когда я определил класс w в A.cpp, я получил ошибку переопределения, которая заставляет меня поверить, что у классов есть внутренняя связь.

Я что-то здесь упускаю?

Ответы [ 5 ]

21 голосов
/ 26 сентября 2014

Правильный ответ - да, имя класса может иметь внешнюю связь.Предыдущие ответы неверны и вводят в заблуждение.Код, который вы показываете, является законным и распространенным.

Имя класса в C ++ 03 может иметь внешнюю связь или не иметь связи.В C ++ 11 имя класса может дополнительно иметь внутреннюю связь.

C ++ 03

§3.5 [basic.link]

Имяговорят, что имеет связь, когда она может обозначать тот же объект, ссылку, функцию, тип , шаблон, пространство имен или значение в качестве имени, введенного объявлением в другой области действия

Классимена могут иметь внешнюю связь.

Имя, имеющее область имен, имеет внешнюю связь, если это имя

[...]

- именованный класс(раздел 9) или безымянный класс, определенный в объявлении typedef, в котором класс имеет имя typedef для целей связывания (7.1.3)

Имена классов не могут иметь связи.

Имена, на которые не распространяются эти правила, не имеют связи.Кроме того, за исключением отмеченного, имя, объявленное в локальной области (3.3.2), не имеет связи.Имя без связи (в частности, имя класса или перечисления, объявленного в локальной области (3.3.2)) не должно использоваться для объявления объекта со связью.

В C ++11 изменения первой кавычки и имена классов в области пространства имен теперь могут иметь внешнюю или внутреннюю связь.

Пространство имен без имени или пространство имен, объявленное прямо или косвенно в пространстве имен без имени, имеет внутреннюю связь.Все остальные пространства имен имеют внешнюю связь.Имя, имеющее область имен, которому не было присвоено внутреннее связывание выше [именами классов не было], имеет ту же связь, что и окружающее пространство имен, если это имя

[...]

- именованный класс (раздел 9) или безымянный класс, определенный в объявлении typedef, в котором класс имеет имя typedef для целей связывания (7.1.3);

Вторая цитата также изменяется, новывод тот же, имена классов могут не иметь связи.

Имена, не охваченные этими правилами, не имеют связи.Кроме того, за исключением отмеченного, имя, объявленное в области видимости блока (3.3.3), не имеет связи.Говорят, что тип имеет связь в том и только в том случае, если:

- это тип класса или перечисления, который назван (или имеет имя для целей связывания (7.1.3)), а имя имеет связь;или

- это неназванный член класса или перечисления класса со связью;

Некоторые ответы здесь объединяют абстрактное понятие связи в Стандарте C ++ с компьютерной программойизвестный как линкер.Стандарт C ++ не придает особого значения символу слова.Символ - это то, что компоновщик разрешает при объединении объектных файлов в исполняемый файл.Формально это не имеет отношения к понятию связи в Стандарте C ++.В документе только когда-либо упоминаются компоновщики в сноске, касающейся кодировки символов.

Наконец, ваш пример является допустимым C ++, а не является нарушением ODR .Рассмотрим следующее.

C.h
----------
class w
{
public:
    w();
};


A.cpp
-----------
#include "C.h"


B.cpp
-----------
#include "C.h"

Возможно, это выглядит знакомым.После того, как директивы препроцессора оценены, мы остаемся с оригинальным примером.Ссылка на Википедию, предоставляемая Alok Save, даже утверждает это как исключение.

Некоторые вещи, такие как типы, шаблоны и внешние встроенные функции, могут быть определены в нескольких единицах перевода.Для данного объекта каждое определение должно быть одинаковым.

Правило ODR учитывает содержание.То, что вы показываете, на самом деле требуется для того, чтобы единица перевода могла использовать класс в качестве завершенного типа.

§3.5 [basic.def.odr]

Ровно одно определениекласс требуется в единице перевода, если этот класс используется таким образом, что тип класса должен быть завершен.

правка - Вторая половина ответа Джеймса Канзе правильно поняла.

5 голосов
/ 24 июня 2011

Технически, как указывает Максим, связь применяется к символам, а не к сущности, которые они обозначают. Но связь символа частично определяется тем, что он обозначает: символы, которые называют классы, определенные в область имен имеет внешнюю связь, а w обозначает одну и ту же сущность в обоих A.cpp и B.cpp.

C ++ имеет два разных набора правил, касающихся определения объекты: некоторые объекты, такие как функции или переменные, могут быть только определяется один раз во всей программе. Определение их более одного раза привести к неопределенному поведению; большинство реализаций будет (большинство время, в любом случае) выдает многократную ошибку определения, но это не обязательно или гарантировано. Другие объекты, такие как классы или шаблоны, должны быть определены в каждой единице перевода, которая их использует, с дальнейшее требование, чтобы каждое определение было идентичным: то же самое последовательность токенов и все символы, привязанные к одной и той же сущности, с очень ограниченное исключение для символов в константных выражениях при условии адрес никогда не берется. Нарушение этих требований также не определено поведение, но в этом случае большинство систем даже не предупредит.

0 голосов
/ 01 апреля 2015

Класс объявление

class w
{
public:
    w();
};

не производит никакого кода или символов, поэтому нет ничего, что могло бы быть связано и иметь "связь".Однако, когда ваш конструктор w () определен как ...

w::w()
{
  // object initialization goes here
}

, он будет иметь внешнюю связь.Если вы определите его как в A.cpp, так и в B.cpp, произойдет конфликт имен;что произойдет, зависит от вашего компоновщика.Линкеры MSVC, например, завершатся с ошибкой LNK2005 «функция уже определена» и / или LNK1169 «найден один или несколько кратно определенных символов».Линкер GNU g ++ будет вести себя аналогично.(Для дублированных методов template вместо этого они исключают все, кроме одного экземпляра; документы GCC называют это « Borland модель»).

Существует четыре способа решенияэта проблема:

  1. Если оба класса идентичны, поместите определения только в один файл .cpp.
  2. Если вам нужны две разные, внешне связанные реализации class w, поместите ихв разные пространства имен .
  3. Избегайте внешних связей, помещая определения в анонимное пространство имен .
namespace
{
  w::w()
  {
    // object initialization goes here
  }
}

Everying in anonymousПространство имен имеет внутреннюю связь, так что вы также можете использовать ее как замену для объявлений static (что невозможно для методов класса).

Избегайте создания символов, определяя встроенные методы:
inline w::w()
{
  // object initialization goes here
}

№ 4 не будет работать, только если в вашем классе нет статических полей (переменных класса), и он будет дублировать код встроенного кода.методы для каждого вызова функции.

0 голосов
/ 24 июня 2011

Классы не имеют связи, которая должна быть педантичной.

Связь применяется только к symbols, то есть к функциям и переменным или коду и данным.

0 голосов
/ 24 июня 2011

Внешняя связь означает, что символ (функция или глобальная переменная) доступен в вашей программе, а Внутренняя связь означает, что это толькодоступно в одном переводчике.Вы явно управляете связыванием символа, используя ключевые слова extern и static, а связывание по умолчанию extern для неконстантных символов и static (internal) для константных символов.

Имя с внешней связью обозначает объект, которыйна него можно ссылаться через имена, объявленные в той же области или в других областях действия той же единицы перевода (так же, как с внутренней связью), или дополнительно в других единицах перевода.

Программа фактически нарушает Правило одного определения но компилятору трудно обнаружить ошибку, потому что они находятся в разных единицах компиляции.И даже компоновщик не может обнаружить это как ошибку.

C ++ позволяет обходить обход Единого правила определения, используя пространство имен .

[ОБНОВЛЕНИЕ] ССтандарт C ++ 03
§ 3.2 Одно правило определения, раздел 5, гласит:

Может быть болееодно определение типа класса ... в программе при условии, что каждое определение появляется в другой единице перевода, и при условии, что определения удовлетворяют следующим требованиям.Если такой объект с именем D определен более чем в одной единице перевода, то каждое определение D должно состоять из одной и той же последовательности токенов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...