Почему функции-члены класса встроены? - PullRequest
26 голосов
/ 16 марта 2012

Я думаю, что мой вопрос задавался здесь раньше, я читал их, но все еще немного запутался и поэтому просил прояснить его.

The C++ standard says all member functions defined inside class definition are inline

Я также слышал, что компиляторможет игнорировать встраивание функции.Будет ли это верно в приведенном выше случае или всегда будет встроенным, если оно определено внутри определения класса?

Кроме того, что послужило причиной такого дизайна, сделав все функции, определенные внутри определения класса, встроенными?И какое отношение встраивание имеет к исходным и заголовочным файлам?

Обновление: Таким образом, всегда следует определять их функции вне класса, если не встраивать, верно?

Обновление 2 от JohnB: Две функции, объявленные внутри определения класса, никогда не смогут вызывать друг друга, поскольку они должны будут содержать все тело другой функции.Что будет в этом случае? (уже ответил Эмилио Гаравалья)

Ответы [ 6 ]

38 голосов
/ 16 марта 2012

Путаница возникает из-за двух эффектов:

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

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

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

Теперь, согласно спецификации языка, свободные функции (не определенные в телах классов) по умолчанию не определены как встроенные, поэтому в заголовке определяется такая вещь, как

void myfunc()
{}

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

inline void fn()
{}

Для учеников по умолчанию все наоборот: если вы просто объявите их, они не будут встроены. Если вы определите их, они будут встроены.

Таким образом, заголовок должен выглядеть как

//header file

class myclass
{
public:
    void fn1()
    {} //defined into the class, so inlined by default

    void fn2();
};

inline void myclass::fn2()
{} //defined outside the class, so explicit inline is needed

И если определение myclass::fn2() входит в правильный источник, оно должно потерять ключевое слово inline.

11 голосов
/ 16 марта 2012

Ключевое слово inline имеет для функции 2 значения:

  1. Замена кода : везде, где вызывается функция inline, не генерируйте для нее вызов функциино просто поместите содержимое функции в место ее вызова (это похоже на замену макроса, но тип безопасен)
  2. Одно правило определения : не создавать множественное определение дляinline функция, генерирует только одно определение, общее для всех (исключение: static функции)

1-ая терминология («Замена кода») - это просто запрос компилятору.который может быть проигнорирован как компилятор, лучше судить, помещать ли текст или вызов функции.(например, virtual функции или рекурсивные функции не могут быть встроенными).

2-ая терминология («Правило одного определения») - гарантируется, что гарантированно произойдет любым соответствующим компилятором.Это создаст только 1 определение для всех единиц перевода.Это средство иногда облегчает работу кодера, так как для более мелкой функции можно не захотеть помещать ее определение в файл .cpp (например, геттеры, сеттеры).
Более того, для функции template, которые являются только конструкциями заголовка, этот эффектобязательный.Таким образом, template функции по умолчанию inline.

Примеры:

class A {
public:
  void setMember (int i) { m_i = i; }
};

В этом примере в большинстве случаев компилятору будет достаточно обеих терминологий

class A {
  inline virtual ~A () = 0;
};
A::~A() {}

Здесь компилятор можетдостаточно только 2-го требования.

8 голосов
/ 16 марта 2012

Единственная причина сделать функцию метода встроенной, если вы определяете ее в заголовке.

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

c ++ 11 стандарт в 9.3 / 2 Функции-члены [class.mfct] говорит:

AФункция-член может быть определена (8.4) в определении класса, и в этом случае это встроенная функция-член (7.1.2) ...

2 голосов
/ 16 марта 2012

Когда определение находится внутри класса, оно обрабатывается так, как если бы оно было объявлено inline, поскольку предполагается, что определения класса находятся в заголовочных файлах, которые используются из более чем одной единицы перевода, поэтому любые не встроенные определенияздесь будет нарушено Правило Единого Определения.

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

Таким образом, короче говоря, она не рассматривается как другаяиз функций, которые явно объявлены inline.

2 голосов
/ 16 марта 2012

Компилятор может игнорировать встраивание, если указано ключевым словом inline. Если реализация метода присутствует внутри определения класса, это другая вещь, и ее нельзя игнорировать. (ну, может, но это делает компилятор несоответствующим)

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

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

РЕДАКТИРОВАТЬ: На примечании стороны, абзац, на который ссылается операция, это 7.1.2.3:

Функция, определенная в определении класса, является встроенной функцией [...].

EDIT2:

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

Таким образом, функция может быть встроенной, но ее тело нельзя вставлять вместо вызова.

0 голосов
/ 16 марта 2012

две вещи, к которым вы обращаетесь, - это разные аспекты, и их не следует путать.

1) Стандарт C ++ говорит, что все функции-члены, определенные внутри определения класса, являются встроенными

2) Я также слышал, что компилятор может игнорировать встраивание функции

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

2) Вы можете указать функцию как встроенную, явно указав ключевое слово inline.это на самом деле запрос к компилятору.компилятор может или не может сделать функцию встроенной в соответствии с некоторыми правилами оптимизации.

...