2012-07-17 2 views
29

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

+0

Вы спрашиваете, почему сами функции должны быть объявлены или вы спрашиваете, почему вы должны сказать «частные» для этих функций? – GManNickG

+0

@GManNickG я думаю первый. –

+2

@GManNickG 1-й. Я просто не знаю, почему другие классы, включая мой заголовок, должны знать о его частных функциях. – Ancurio

ответ

21

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

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

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

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

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


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

Это, в частности, элементы-члены.

+1

Да, я привык скрывать реализацию на C с помощью статических функций C, поэтому я бы просто объявил (до любая реализация) частные функции внутри cpp отдельно от остальных, так же как статические объявления в C – Ancurio

+0

Случай использования «друга», однако, является допустимой точкой. Благодарю. – Ancurio

+0

@ Анукурио, хотя то, что вы говорите, имеет смысл, к сожалению, нет поддержки от C++, чтобы нарезать определение класса на две части. – Shahbaz

2

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

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(); 
} 
+0

Эх, это только указывает, что правила перегрузки должны измениться. – GManNickG

+0

@GManNickG Чтобы получать видимость во внимание и не выбирать недоступные методы? У C++ есть все основания для того, как он работает. Например: http://stackoverflow.com/questions/644461/c-constructor-overloading-private-and-public – bames53

+0

Я не думаю, что правила перегрузки должны были бы учитывать доступность. Они, вероятно, просто должны быть определены, чтобы учитывать только функции-члены, которые объявляются перед точкой использования, поэтому, если ваша функция частного члена находится в определении класса, то она останавливает 'aF (1)' компиляцию в клиентском коде , и если он добавлен только в 'A.cpp', то это не влияет на код клиента, но влияет на .cpp. Это уже то, как работают пространства имен (если вы добавляете больше перегрузок, они могут быть выбраны). Требуется некоторая сложность, например, что происходит с двухфазным поиском. –

0

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

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

+1

Да, но _why спецификация говорит, что они должны быть объявлены? Вы не ответили на вопрос. Там уже есть правила, говорящие, что функции должны быть объявлены перед вызовом, так что это не так. (совет на языке имен хороший, хотя) –

20

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

+9

Я думаю, что вы только что описали классы Python =) – JoeFish

0

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

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

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

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

Взятые из стандарта C++:

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

3-й Друзья

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

четвёртая Общие здравомыслие и Good Design

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

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

+3

Это не хочет, чтобы Анкурио хотел знать. Он задается вопросом, почему мы не можем (некоторые) держать частные функции в качестве * секретного * в первую очередь. Вы не можете вызвать функцию, о которой вы не знаете. –

+0

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

+1

@nate_weldon, это правда, но если частные не виртуальные функции не были объявлены в общедоступном заголовочном файле (и, следовательно, документация), в первую очередь, никто не попытался бы назвать это. За исключением, может быть, для себя ... в этом случае это предупреждение (не правда ли это на самом деле?) Имеет смысл. – Ancurio

3

Там это сочетание проблем, но:

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

Поэтому:

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

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

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

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

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

+0

Текущие предложения модулей имеют решения проблемы конфиденциальности, просто потому, что они добавляют еще один «слой» или «комнату» видимости: сам класс, модуль, «внешний код». 'private' будет просто не утечка вне определения модуля, и компилятор все еще может знать обо всем. – Xeo

+0

«_C++ не позволяет вам повторно открывать класс для объявления новых членов в нем после его начального определения.» «Это неверно: 14.7.3 ** Явная специализация **' [temp.expl.spec] '" Явная специализация любого из следующего: (...) - шаблон класса-члена шаблона класса или класса - шаблон функции-члена шаблона класса или класса может быть объявлен объявлением (...) "" явная специализация объявляется в пространстве имен, охватывающих специализированный шаблон ». – curiousguy

+0

@curiousguy: не уверен, что я последую за тобой. Я не думаю, что специализация шаблона-члена является «новым членом», так как шаблон является членом. Возможно, я ошибаюсь. Если я ошибаюсь, то, конечно, шаблоны являются исключением из того, что я говорил, поскольку так же, как специализированные шаблоны членов вне класса, вы можете, конечно, также * создавать экземпляры * шаблонов-членов вне класса. В любом случае я не думаю, что класс был «вновь открыт», и я не думаю, что он помогает эксперту. –

1

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

+0

@Neal: Ну ... это чертова цель * друга. – Xeo

Смежные вопросы