#c #oop
#c #ооп
Вопрос:
В чем разница между функциями-членами и функциями, не являющимися членами в C ?
Комментарии:
1. Интересно, я думал, что ответы будут довольно сухими, но, похоже, у всех нас очень разные мнения по этому поводу!
Ответ №1:
Существует несколько различий между функцией-членом (которую я сейчас вызову методом) и свободной функцией (которую я сейчас вызову function).
Во-первых, давайте просто констатируем, что они не такие разные. Объектный код обычно может быть скомпилирован вплоть до C (или assembly), которые являются процедурными языками без понятия методов. Затем оба метода и функции вызываются как подпрограммы.
Теперь, когда с этим покончено, давайте посмотрим на различия. Их можно разделить на две категории: концептуальные и синтаксические.
Синтаксически
Синтаксис является очевидной частью любого языка, поэтому с ним проще всего разобраться.
Первое примечание: в C (и ряде других языков) существует два разных вида методов: static
методы и обычные методы.
Оба вида методов имеют полный доступ к внутренним элементам класса ( protected
и private
разделам), а также (конечно) доступ к public
интерфейсу класса.
static
методы эквивалентны friend
функциям (за исключением некоторых различий в области видимости).
В рамках обычного метода специальное ключевое слово ( this
в C ) разрешает доступ к текущему объекту, для которого был вызван метод (с помощью операторов .
, ->
, .*
или ->*
).
Обычный метод может быть const
и / или volatile
квалифицирован, что позволяет использовать его для (соответственно) const
и / или volatile
квалифицированных объектов. Например, для const
объекта не может быть вызван const
метод. Это можно рассматривать как квалификацию this
внутри метода, т.е. void Foo::bar() const
имеет this
тип Foo const*
of.
Обычный метод может быть помечен как virtual
. Виртуальность обеспечивает полиморфизм во время выполнения, позволяя переопределять. Я не буду здесь распространяться об этом механизме, давайте просто отметим, что функции не могут быть виртуальными.
Один часто игнорируемый момент заключается в том, что методы (как static
, так и обычные) ограничены внутри класса. Это важно для поиска имен (других методов или атрибутов / переменных), поскольку это означает, что элементы класса имеют приоритет при поиске из метода элементов, объявленных вне класса.
Поскольку квалификация с использованием this->
атрибута before или методов не является обязательной, это удобно в обычных методах, хотя может привести к незначительным ошибкам. В статических методах это позволяет избежать определения именем класса статических атрибутов и методов, к которым требуется получить доступ.
Теперь, когда основные синтаксические различия были заявлены, давайте проверим концептуальные различия.
Концептуально
ООП обычно заключается в связывании состояния и поведения (этого состояния). Это делается путем создания классов, которые группируют атрибуты (состояние) и поведение (методы) и (теоретически) заявляют, что только методы могут воздействовать на состояние. Следовательно, в ООП методы отвечают за реализацию поведения класса.
Методы участвуют в инкапсуляции состояния (освобождая клиентов от деталей реализации) и в сохранении инвариантов класса (утверждений о состоянии класса, которые остаются верными от его рождения до его смерти, что бы вы с ним ни делали).
C
В C , как мы видели ранее, это делается путем использования разных уровней доступа ( public
, protected
и private
) и предоставления доступа к не- public
уровням к ограниченной части кода. Обычно атрибуты будут закрытыми и, следовательно, доступными только для методов класса (и, возможно, некоторых друзей, из-за особенностей синтаксиса).
Примечание: Я настоятельно рекомендую вам не использовать protected
атрибуты, трудно отследить их модификации, а поскольку набор производных классов неограничен … их реализацию впоследствии нелегко изменить.
Однако имейте в виду, что C не поощряет раздувание интерфейса множеством методов.
Проблема в том, что, поскольку методы отвечают за поддержание инвариантов, чем их больше, тем больше распределяется ответственность, что затрудняет отслеживание ошибок и обеспечение корректности. Кроме того, поскольку методы зависят от внутренних компонентов класса, это делает изменения более дорогостоящими.
Вместо этого в C обычно рекомендуется писать минимальный набор методов и делегировать остальное поведение не friend
функциям (при условии, что это не слишком увеличивает стоимость).
- Посмотрите, как Саттер относится к
std::string
в Monolith Unstrung. - Делегирование чужим методам было подчеркнуто Саттером в его Принципе интерфейса, в котором он заявляет, что функции, которые поставляются вместе с классом (в том же файле / том же пространстве имен) и используют класс, логически являются частью интерфейса класса. Он повторяет в исключительном C .
Этот ответ становится довольно многословным, но я подозреваю, что я упустил из виду различия, которые другие сочли бы критическими … ну что ж.
Ответ №2:
(Нестатическая) функция-член имеет неявный this
аргумент, не являющаяся членом — нет.
Синтаксически вы передаете этот неявный аргумент слева от оператора .
or ->
like.so()
или like->so()
, а не как аргумент функции so( like )
.
Аналогично, при объявлении функции-члена вам нужно сделать это в классе, членом которого она является:
class Class {
public:
void a_public_member_function();
};
Вместо этого функции, не являющиеся членами, объявляются вне какого-либо класса (C называет это «в области пространства имен»).
(Нестатические) функции-члены также могут быть виртуальными, но функции, не являющиеся членами (и статические функции-члены), не могут.
Комментарии:
1. 1 за четкий ответ и за то, что не путаете «нестатические» со «статическими» функциями-членами.
Ответ №3:
Функция, не являющаяся static
членом, вызывается для объектов класса, к которому она принадлежит. Он имеет неявный доступ к this
указателю, представляющему текущий объект. С помощью этого указателя он может легко обращаться к другим членам с полными привилегиями доступа (т.Е. получать доступ к private
членам).
Функция, не являющаяся членом, не имеет неявного значения this
. В приведенном ниже примере bar
является функцией-членом, а freebar
нет. Обе функции выполняют более или менее одно и то же, но обратите внимание, как bar
получает неявный указатель на объект через this
(также только bar
имеет привилегированный доступ к foo
членам, freebar
имеет доступ только к общедоступным членам).
class foo {
public:
void bar() {
this->x = 0; // equivalent to x = 0;
}
int x;
};
void freebar(foo* thefoo) {
thefoo->x = 1;
}
// ...
foo f;
f.bar();
// f.x is now 0
freebar(amp;f);
// f.x is now 1
Семантически функция-член — это нечто большее, чем просто функция с неявным параметром this. Он предназначен для определения поведения объекта (т. Е. объект car будет иметь drive()
, stop()
в качестве функций-членов).
Обратите внимание, что также существуют static
функции-члены, которые обладают полными привилегиями, но не получают неявного this
значения и не вызываются через экземпляр класса (а скорее через полное имя класса).
Комментарии:
1.
this
это ошибка реализации языка. Лично я бы подчеркнул взаимосвязь метода, действующего на конкретный объект, а не фокусировался на том, как эта взаимосвязь реализована. В 99,9% случаев вы на самом деле не используете это, но вы все равно взаимодействуете с членами объекта (единственное место, где я использую это в operator=, чтобы вернуть ссылку на self ).2. Я немного отредактировал свой текст, сделав акцент на семантическом значении функции-члена. H
3. @Martin Это проблема с реализацией, но это важно для понимания различий и их последствий в C .
4. Я думаю, что это требует более тонкого разграничения. Функция-член имеет доступ только к
this
, если она нестатична.5. Добавлено примечание о
static
функциях-членах.
Ответ №4:
В следующем коде f()
является функцией-членом класса Sample
и g()
является функцией, не являющейся членом:
class Sample
{
void f();
};
void g();
Это очень просто. Поскольку f()
является членом класса Sample
, поэтому его называют функцией-членом (класса Sample
). И поскольку g()
не является членом какого-либо класса, поэтому его называют функцией, не являющейся членом.
Ответ №5:
Функция-член вызывается для объекта и имеет доступ к полям класса.
Функции-члены могут быть полиморфными (через virtual
ключевое слово), что важно для ООП.
Ответ №6:
Функции-члены вызываются в экземплярах и имеют this
доступный указатель; не являющиеся членами — нет.
Комментарии:
1. означает ли это, что все функции класса являются функциями-членами, за исключением статических функций?
2. @Luke: Да. (Я пишу это, думая, что кто-нибудь собирается указать на что-то неясное, что я забыл). PS. Я не твой отец (4 мая по вчерашнему дню)
3. @Luke, нет, это не так. Те ответы, которые путают статические функции-члены с нестатическими, неверны. Правильное описание смотрите в ответе @mmutz.