Почему класс extern "C" С++ (не заголовок) здесь?

Я искал библиотеки SVM и встречал BudgetedSVM.

В исходном коде я нашел необычное использование, как это:

#sample.h

#ifndef SAMPLE_H
#define SAMPLE_H

//no header included or namespace declared here

#ifdef __cplusplus
extern "C" {
#endif

//no header included or namespace declared too

class Sample: public Parent
{
public:
    Sample();
    ~Sample();

    type0 fun(type1 val1, type2 val2);
    ...
};

#ifdef __cplusplus
}
#endif

#endif // SAMPLE_H

Как видно, в заголовке не требуется дополнительный заголовок или пространство имен, все они находятся в файле cpp.

Вот мои мысли:

  • Почему extern "C", который обычно используется для интерфейсов C, группирует класс С++? Является ли использование таким образом хорошим для чего-то?

  • Даже если появились type0, type1 и type2, их собственные заголовки здесь не включены, а в файл cpp (например, sample.h). Однако, когда я вызываю класс Sample, я должен включать эти заголовки (например, type0.h, type1.h, type2.h), что кажется неудобным.

Ответ 1

Согласно С++ Standard C языковая связь игнорируется при определении языковой привязки имен членов класса и типа функции функций члена класса.

Таким образом, такая группировка выглядит бессмысленной, если type0, type1 или type2 не являются указателями на функции, определенными в строке, в этом случае они будут иметь C-ссылку.

В случае type0, type1 или type2 являются определяемыми пользователем типами, а их определения помещаются в отдельные заголовки, это нарушает правильное правило стиля C/С++ - все заголовки должны быть автономными и не требуют дополнительных включений для компиляции.

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

Ответ 2

Даже если появились типы0, type1 и type2, их собственные заголовки здесь не включены, а в файл cpp (например, sample.h). Однако, когда я вызываю образец класса, я должен включать этот заголовок (например, type0.h, type1.h, type2.h), что кажется неудобством.

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

Заголовок не должен включать ничего, кроме минимума, который сводит его собственное включение. Другие вещи должны быть объявлены только до тех пор, пока это возможно. Для этой цели стандартная библиотека имеет заголовок iosfwd. Таким образом, для классов, объявляющих операторы потока (<< и >>), не потребуется вытягивать весь заголовок iostream.

Сайт вызова, который также знает, какой конкретный поток он хочет записать или прочитать, делает включение.

Помните философию дизайна С++: вы не платите за то, что вам не нужно.

Ответ 3

С точки зрения C есть несколько причин для этого (ваш вопрос 2). Один из них скрывает данные, другой - улучшает время компиляции, уменьшая иерархию include.

И C позволяет использовать указатели типов (передавая их в функции и т.д.), не зная о них/включая их. Если вы не разыгрываете, это прекрасно. Таким образом, вы оставляете детали структуры для функций-исполнителей, абстрагируя детали.

Еще одно объяснение из книги, раздел 4.8 проверки: http://www.duckware.com/bugfreec/chapter4.html#cppcomparison

Ответ 4

Я считаю, что это используется при попытке создать библиотеку, к которой может быть привязан стандартный C-компоновщик.

Заголовки обычных символов C будут закодированы с именем и типом, так что функция вроде этого:

int blah(double num);

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

Одна вещь, с которой работает extern C, - это заставить ее искать символы в обычной моде C, которые были бы просто "бла"

Я думаю, например, это используется для создания допустимого заголовка С++, который пытается использовать его компилятор, создаст ссылку на символы в другой библиотеке C.

Без extern C он будет искать общий объект библиотеки с именем функции/закодированным символом в стиле С++.

Итак, чтобы сделать его более понятным, это либо используется

  • создать С++ API для переноса библиотеки C.

или

  • для создания библиотеки С++, к которой может быть привязано приложение C

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