Подтвердить что ты не робот

С++: зачем объявлять частные функции?

Почему классы на С++ должны объявлять свои частные функции? Имеет ли это фактические технические причины (какова его роль во время компиляции) или просто для согласованности?

4b9b3361

Ответ 1

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

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

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

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

Наконец, обратите внимание, что функции private могут влиять на размер vtable. То есть, если они virtual.


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

2 В частности, это встроенные функции-члены.

Ответ 2

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

Ответ 3

Есть комбинация проблем, но:

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

Таким образом:

  • Любые частные функции-члены, которые хотят получить файл .cpp, объявленный в классе, должны быть определены в файле .h, который тоже видит каждый пользователь класса.

Из POV практической двоичной совместимости: как говорит Дэвид в комментарии, частные функции virtual влияют на размер и макет vtable этого класса и любых классов, которые используют его в качестве базы. Поэтому компилятор должен знать о них даже при компиляции кода, который не может их назвать.

Может ли С++ быть изобретен иначе, чтобы позволить .cpp файлу снова открыть класс и добавить определенные виды дополнительных функций-членов, с реализацией, требуемой для организации того, чтобы это не нарушало двоичную совместимость? Можно ли смягчить одно правило определения, чтобы определенные определения отличались определенным образом? Например, статические функции-члены и не виртуальные нестатические функции-члены.

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

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

Ответ 4

Уровень доступа не влияет на видимость. Частные функции видны внешнему коду и могут быть выбраны с помощью разрешения перегрузки (что приведет к ошибке доступа к виолончели):

class A {
    void F(int i) {}
public:
    void F(unsigned i) {}
};

int main() {
    A a;
    a.F(1); // error, void A::F(int) is private
}

Представьте себе замешательство, когда это работает:

class A {
public:
    void F(unsigned i) {}
};

int main() {
    A a;
    a.F(1);
}

// add private F overload to A
void A::F(int i) {}

Но изменение его на первый код вызывает разрешение перегрузки для выбора другой функции. А как насчет следующего примера?

class A {
public:
    void F(unsigned i) {}
};

// add private F overload to A
void A::F(int i) {}

int main() {
    A a;
    a.F(1);
}

Или вот еще один пример этого не так:

// A.h
class A {
public:
    void g() { f(1); }
    void f(unsigned);
};

// A_private_interface.h
class A;
void A::f(int);

// A.cpp
#include "A_private_interface.h"
#include "A.h"

void A::f(int) {}
void A::f(unsigned) {}

// main.cpp
#include "A.h"

int main() {
    A().g();
}

Ответ 5

Одна из причин заключается в том, что в С++ друзья могут получить доступ к вашим рядовым. Друзья должны получить доступ к ним, друзья должны знать о них.

Ответ 6

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

Если у вас есть метод, который используется только в файле .cpp и не зависит от прямого доступа к другим закрытым членам класса, подумайте о переносе его в анонимное пространство имен. Затем его не нужно объявлять в файле заголовка.

Ответ 7

Есть несколько причин, почему частные функции должны быть объявлены.

Первые ошибки проверки времени компиляции

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

Второе литье и наследование

Взято из стандарта С++:

3 [Примечание. Член частного базового класса может быть недоступен как имя наследуемого члена, но доступен напрямую. Из-за правил конверсий указателей (4.10) и явных приведений (5.4) преобразование из указателя в производный класс в указатель на недоступный базовый класс может быть плохо сформировано, если используется неявное преобразование, но хорошо сформированное если используется явный приём.

Друзья 3

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

Четвёртое общее здравомыслие и хороший дизайн

Когда-либо работал над проектом с еще 100 разработчиками. Наличие стандартного и общего набора правил помогает поддерживать ремонтопригодность. объявление чего-то частного имеет конкретное значение для всех остальных в группе.

Также это вливается в хорошие принципы проектирования OO. Что разоблачить, а что нет