Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп
Шрифт:
Интервал:
Закладка:
int old_style(p,b,x) char* p; char b;
{
/* ... */
}
Это определение “в старом стиле” предвосхищает конструкции языка С++ и не является прототипом. По умолчанию аргумент без объявленного типа считается аргументов типа int. Итак, параметр x является аргументом функции old_style(), имеющим тип int. Мы можем вызвать функцию old_style() следующим образом:
old_style(); /* OK: пропущены все аргументы */
old_style("hello",'a',17); /* OK: все аргументы имеют правильный тип */
old_style(12,13,14); /* OK: 12 — неправильный тип */
/* но old_style() может не использовать p */
Компилятор должен пропустить эти вызовы (но мы надеемся, что он предупредит о первом и третьем аргументах).
Мы рекомендуем придерживаться следующих правил проверки типов аргументов функций.
• Последовательно используйте прототипы функций (используйте заголовочные файлы).
• Установите уровень предупреждений компилятора так, чтобы перехватывать ошибки, связанные с типами аргументов.
• Используйте (какую-нибудь) программу lint.
В результате вы получите код, который одновременно будет кодом на языке C++.
27.2.4. Вызов функций, написанных на языке С, из программы на языке С++, и наоборот
Вы можете установить связи между файлами, скомпилированными с помощью компилятора языка С, и файлами, скомпилированными с помощью компилятора языка С++, только если компиляторы предусматривают такую возможность. Например, можете связать объектные файлы, сгенерированные из кода на языке С и С++, используя компиляторы GNU C и GCC. Можете также связать объектные файлы, сгенерированные из кода на языке С и С++, используя компиляторы Microsoft C и C++ (MSC++). Это обычная и полезная практика, позволяющая использовать больше библиотек, чем это возможно при использовании только одного из этих языков.
В языке C++ предусмотрена более строгая проверка типов, чем в языке C. В частности, компилятор и редактор связей для языка C++ проверяют, согласованно ли определены и используются функции f(int) и f(double), даже если они определены в разных исходных файлах. Редактор связей для языка C не проводит такой проверки. Для того чтобы вызвать функцию, определенную в языке C, в программе, написанной на языке C++, и наоборот, необходимо сообщить компилятору о том, что вы собираетесь сделать.
// вызов функции на языке C из кода на языке C++:
extern "C" double sqrt(double); // связь с функцией языка C
void my_c_plus_plus_fct()
{
double sr = sqrt(2);
}
По существу, выражение extern "C" сообщает компилятору о том, что вы используете соглашения, принятые компилятором языка С. Помимо этого, с точки зрения языка С++ в этой программе все нормально. Фактически стандартная функция sqrt(double) из языка C++ обычно входит и в стандартную библиотеку языка С. Для того чтобы вызвать функцию из библиотеки языка С в программе, написанной на языке С++, больше ничего не требуется. Язык C++ просто адаптирован к соглашениям, принятым в редакторе связей языка C.
Мы можем также использовать выражение extern "C", чтобы вызвать функцию языка С++ из программы, написанной на языке С.
// вызов функции на языке C++ из кода на языке C:
extern "C" int call_f(S* p, int i)
{
return p–>f(i);
}
Теперь в программе на языке C можно косвенно вызвать функцию-член f().
/* вызов функции на языке C++ из функции на языке C: */
int call_f(S* p, int i);
struct S* make_S(int,const char*);
void my_c_fct(int i)
{
/* ... */
struct S* p = make_S(x, "foo");
int x = call_f(p,i);
/* ... */
}
Для того чтобы эта конструкция работала, больше о языке С++ упоминать не обязательно.
Выгоды такого взаимодействия очевидны: код можно писать на смеси языков C и C++. В частности, программы на языке C++ могут использовать библиотеки, написанные на языке C, а программы на языке C могут использовать библиотеки, написанные на языке С++. Более того, большинство языков (особенно Fortran) имеют интерфейс вызова функций, написанных на языке С, и допускают вызов своих функций в программах, написанных на языке С.
В приведенных выше примерах мы предполагали, что программы, написанные на языках C и C++, совместно используют объект, на который ссылается указатель p. Это условие выполняется для большинства объектов. В частности, допустим, что у нас есть следующий класс:
// В языке C++:
class complex {
double re, im;
public:
// все обычные операции
};
Тогда можете не передавать указатель на объект в программу, написанную на языке С, и наоборот. Можете даже получить доступ к членам re и im в программе, написанной на языке C, с помощью объявления
/* В языке C: */
struct complex {
double re, im;
/* никаких операций */
};
Правила компоновки в любом языке могут быть сложными, а правила компоновки модулей, написанных на нескольких языках, иногда даже трудно описать. Тем не менее функции, написанные на языках C и C++, могут обмениваться объектами встроенных типов и классами (структурами) без виртуальных функций. Если класс содержит виртуальные функции, можете просто передать указатели на его объекты и предоставить работу с ними коду, написанному на языке C++. Примером этого правила является функция call_f(): функция f() может быть virtual. Следовательно, этот пример иллюстрирует вызов виртуальной функции из программы, написанной на языке C.
Кроме встроенных типов, простейшим и наиболее безопасным способом совместного использования типов является конструкция struct, определенная в общем заголовочном файле языков C и C++. Однако эта стратегия серьезно ограничивает возможности использования языка С++, поэтому мы ее не рекомендуем.
27.2.5. Указатели на функции
Что можно сделать на языке С, если мы хотим использовать объектно-ориентированную технологию (см. разделы 14.2–14.4)? По существу, нам нужна какая-то альтернатива виртуальным функциям. Большинству людей в голову в первую очередь приходит мысль использовать структуру с “полем типа” (“type field”), описывающим, какой вид фигуры представляет данный объект. Рассмотрим пример.