2010-11-13 3 views
56

Чистые виртуальные функции являются функциями-членами, которые являются виртуальными и имеют чисто-спецификатор (= 0;)Чистые виртуальные функции могут не иметь встроенного определения. Зачем?

Статья 10.4 пункт 2 из C++ 03 говорит нам, что абстрактный класс и, как примечание стороны, следующее:

[Примечание: объявление функции не может обеспечить как чисто-спецификатор и определение -end примечание] [Пример:

struct C { 
virtual void f() = 0 { }; // ill-formed 
}; 

-end пример]

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

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

Мой вопрос: кто-нибудь знаю эти конкретные причины? Хорошо Догадки также приветствуются.

Примечания:

  • MSVC Допускает ПВФ, чтобы иметь определения инлайн. Так что не удивляйтесь :)
  • слово inline в этом вопросе не относится к ключевому слову inline. Это должно означать лексический в классе
+1

Выглядит немного странно при использовании функции try block: 'virtual void f() = 0 try {} catch (...) {}' –

+2

Вот угадай: было проще создать компилятор, который не разрешить его и, учитывая его очень редкое использование, было легким решением. Тот факт, что некоторые компиляторы (вы цитировали MSVC) действительно позволяют это просто означает, что некоторые авторы компилятора не были обеспокоены дополнительной работой. – Tergiver

+0

@Johannes: Что в этом странно? Функциональные блоки try сами по себе странные :) –

ответ

51

В потоке SO “Why pure virtual function is initialized by 0?” Джерри Коффин привел эту цитату из Bjarne Struustrup ’ s The Design & Evolution of C++, раздел §13.2.3, где я добавил некоторые акцент части я считаю уместным:

Любопытная =0 синтаксис был выбран более Очевидной альтернативой введения нового ключевого слова в чистом виде или абстрактный, потому что в то время я видел нет шансов получить новое ключевое слово принятое. Если бы я предложил чистый, выпуск 2.0 имел бы , отправленный без абстрактных классов. Учитывая выбор между хорошим синтаксисом и абстрактными классами, я выбрал абстрактные классы . Вместо того, чтобы рисковать задержкой и , совершая определенные бои за , я использовал традицию C и C++ соглашение об использовании 0 для представления «нет». Синтаксис =0 вписывается моего взгляда, что тело функции является инициализатора для функции, а также с (упрощенно, но обычно достаточно) вида множества виртуальных функций реализуются как вектор функции указатели. [& hellip; ]

Таким образом, при выборе синтаксиса Бьярне думает о теле функции как своего рода инициализатора части описателя и =0 в качестве альтернативной формы инициализаторе, тот, который указанный “ нет тела ” (или в его слова, “ нет ”).

Понятно, что нельзя указать “ нет ” и иметь кузов – в этой концептуальной картине.

Или, еще в том концептуальном изображении, имея два инициализатора.

Теперь это касается моих телепатических способностей, google-foo и мягких рассуждений. Я догадываюсь, что никто не интересовался ™, чтобы сформулировать предложение комитету о том, чтобы это чисто синтаксическое ограничение было отменено, и следование всем работам, которые это влечет за собой. Таким образом, это все еще так.

+14

Wow ... 21 голосов (до сих пор), и если я ничего не пропустил, вы не сказали ни слова, чтобы ответить на вопрос (на самом деле это почему-то вне очереди определения возможны, но не строятся). –

+2

цитаты являются мощными :) – Default

+7

@ Тони: Извините, что вы не понимали значимости моего ответа. Я сделал все что было в моих силах. Это не сработало для вас. Я не знаю, как сказать это более четко. Возможно, между нами существует коммуникационный разрыв. –

8

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

+1

Согласно D & E, чистые виртуальные машины были добавлены в C++ компанией Stroustrup, обсудив ее с собственными пользователями C++ на AT & T, как последнее, всего за несколько недель до выпуска cfront 2.0, который был отправлен в июне 1989 года. В том же источнике, первая встреча ANSI X3J16 (которая позже присоединилась к ISO WG21, чтобы сформировать то, что мы теперь знаем как стандартный комитет) состоялась через полгода, в декабре 1989 года. (Я думал, что я прочитал причину не допускать встроенных определений для абстрактных функции, но я не мог найти его в D & E.) Так что это не было принято Стандартным комитетом. – sbi

+1

@AProgrammer: Спасибо за хорошее предположение. Но я все еще жду ответа, начиная с «Итак, да, я сегодня позвонил Бьярне, и он сказал ...» :) –

+11

Итак, сегодня я позвонил Бьярне, но он не подобрал.;-) –

2

Хорошие догадки приветствуются, вы говорите?

Я думаю, что = 0 в декларации происходит от реализации. Скорее всего, это определение означает, что вы получаете запись NULL в RTTI vtbl информации о классе - место, где хранятся адреса времени функций класса класса.

Но на самом деле, когда поставить определение функции в файле *.cpp, вы вводите имя в объектный файл для линкера: адрес в файле *.o, где найти определенную функцию.

Базовый линкер тогда должен знать о C++ больше. Он может просто соединяться вместе, хотя вы заявили его как = 0.

Думаю, я читал, что возможно то, что вы описали, хотя я забыл о поведении: -) ...

7

Хорошие догадок ... хорошо, учитывая ситуацию:

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

Мое предположение: использование тел для чистых виртуальных функций было реализовано после того, как была сформулирована грамматика = 0 | { ... }, и грамматика просто не была пересмотрена. Стоит подумать, что есть много предложений по языковым изменениям/улучшениям - в том числе, чтобы сделать такие вещи более логичными и последовательными - но число, которое кого-то подхватывает и записывает как официальные предложения, намного меньше, а число из тех, которые у Комитета есть время для рассмотрения, и полагает, что производители компиляторов будут готовы к реализации, намного меньше. Такие вещи нуждаются в чемпионе, и, возможно, вы первый человек, который видит в нем проблему. Чтобы почувствовать этот процесс, ознакомьтесь с http://www2.research.att.com/~bs/evol-issues.html.

0

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

Единственный способ получить вызов реализации - использовать синтаксис Base :: func() из одной из перегрузок производного класса.

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

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

(Кстати, я в предположении, что Base::f() можно назвать только с этим синтаксисом из Derived::f(), а не из Derived::anyOtherFunc(). Правильно ли я с этим предположением?).

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

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

+2

, что предположение о невозможности вызвать его из 'Derived :: anyOtherFunc()' неверно – sp2danny

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