c : разница между функциями-членами и функциями, не являющимися членами

#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.