нужно ли const?
void function(const string &);
или
void getname() const;
какая разница между
void function(const string &) и
void function(string)
или
void getname() const и
void getname().
void function(const string & str)
ты не сможешь внутри function сделать str = "abcd";
Код:
void getname() const;
void function(const string & str)
ты не сможешь внутри function сделать str = "abcd";[/QUOTE]
да я это понимаю но почему давать функции string & если его не надо менять а потом приписывать const?
дадим прямо string?
дадим прямо string?[/QUOTE]
Потому, что если просто передавать string, то будет создан временный объект, а это нежелательно по нескольким причинам:
1) увеличивает время выполнения операции, т.к. вызывается конструктор копирования,
2) в общем случае приводит к фрагментации памяти,
3) в некоторых случаях приводит к неверной логике работы или даже к ошибочным ситуациям, например, когда создание временного объекта приводит к попытке залочить уже залоченный ресурс, к примеру, открыть на запись уже открытый для записи файл.
Код:
void function(const string & str)
Как бы дает гарантию, что передаваемый объект не будет меняться внутри метода.
А у константного объекта (скажем переданного в другой метод описанным выше способом) можно вызывать только константные методы типа такого:
Код:
void getname() const;
Но естественно константный метод не имеет права менять данные класса.
Так что все таки это хороший тон описывать любой публичный метод не меняющий содержимое класса как константный. Ведь было бы странно еслиб у константного объекта мы не могли вызвать метод getName();
aks
спосибо друзья сейчас всё понятно.
это мне многое прояснило.
Хорошо тебе, pod. А вот мне всё равно непонятно. Вот, например, у нас функция void some_func(const char *str). В ней мы не можем производить запись в переменную str. Так? Ну и что это нам даёт? А если бы не было const и запись в str была разрешена, что бы это изменило? Ведь функции сообщается не сама переменная, а её копия, так что никакого сбоя в программе вроде бы быть не должно.
Объясните, пожалуйста, те, кто ещё не вызверился на меня.
Объясните, пожалуйста, те, кто ещё не вызверился на меня.[/QUOTE]
В данном случае передается указатель на константную строку (на строку, а не на её копию). Т.е. читать то ты строку можешь, а изменять её нет.
Саму передаваемую переменную,- указатель, можешь менять сколько угодно, приращивать, заставлять указывать на другие области памяти, на вызывающий код это никак не повлияет.
Если бы в данном примере не было const, то была бы возможность изменять строку. Как следствие константные строки передать в такую функцию нельзя.
P.S. очередная провокация?
Тебе уже так объяснили, что если даже и теперь не понятно, то я бы на твоем месте все равно сказал, бы, что: "всем спасибо, все ясно". Но чтоб лучше понять, откуда упал этот const, советую прочитать из книги Дж.Хеллер-"Ловушка 22" главу о Майор Майор(ту часть в которой он доврался до власти и установил правила в столовой).
Код:
{
AnsiString txt = "Hello, World!";
Show(txt);
}
void Show(AnsiString txt){
ShowMessage(txt);
}
AnsiString txt = "Hello, World!";
Show(txt);
}
void Show(AnsiString txt){
ShowMessage(txt);
}
AnsiString - объект класса. Значит если я указываю его в параметрах, то перед вызовом создасться временный объект класа AnsiString, куда скопируется строка txt ?
Предположим я не буду менять строку, но хочу оптимизации. Если я напишу так:
Код:
AnsiString txt = "Hello, World!";
Show(txt);
}
void Show(const AnsiString txt){
ShowMessage(txt);
}
Show(txt);
}
void Show(const AnsiString txt){
ShowMessage(txt);
}
Временный класс не создастся и я буду работать с реальным указателем ?
Предположим я не буду менять строку, но хочу оптимизации. Если я напишу так:
Код:
AnsiString txt = "Hello, World!";
Show(txt);
}
void Show(const AnsiString txt){
ShowMessage(txt);
}
Show(txt);
}
void Show(const AnsiString txt){
ShowMessage(txt);
}
Временный класс не создастся и я буду работать с реальным указателем ?[/QUOTE]
void Show(const AnsiString[COLOR=RED]&[/COLOR] txt)
ты будешь работать с объектом созданным в вызывающем коде
Код:
void __fastcall TForm1::Button6Click(TObject *Sender)
{
AnsiString txt = "Hello, World!";
Msg(&hmm);
}
void __fastcall TForm1::Msg(AnsiString *txt)
{
ShowMessage(*txt);
}
{
AnsiString txt = "Hello, World!";
Msg(&hmm);
}
void __fastcall TForm1::Msg(AnsiString *txt)
{
ShowMessage(*txt);
}
Тоже оптимально ? Тогда значит const в принципе нужен только для того, чтобы контролировать изменяется ли объект или нет ?
Код:
void __fastcall TForm1::Button6Click(TObject *Sender)
{
Mss(NULL);
}
void __fastcall TForm1::Msg(AnsiString *txt)
{
ShowMessage(*txt);
}
{
Mss(NULL);
}
void __fastcall TForm1::Msg(AnsiString *txt)
{
ShowMessage(*txt);
}
Код:
void __fastcall TForm1::Button6Click(TObject *Sender)
{
const AnsiString hmm = "Hello, World!";
Mss(hmm);
}
void __fastcall TForm1::Msg(AnsiString& txt)
{
ShowMessage(txt);
}
{
const AnsiString hmm = "Hello, World!";
Mss(hmm);
}
void __fastcall TForm1::Msg(AnsiString& txt)
{
ShowMessage(txt);
}
Думаю, понятно что к чему?
Если нет, могу объяснить.
Код:
void __fastcall TForm1::Button6Click(TObject *Sender)
{
Mss(NULL);
}
void __fastcall TForm1::Msg(AnsiString *txt)
{
ShowMessage(*txt);
}
{
Mss(NULL);
}
void __fastcall TForm1::Msg(AnsiString *txt)
{
ShowMessage(*txt);
}
Код:
void __fastcall TForm1::Button6Click(TObject *Sender)
{
const AnsiString hmm = "Hello, World!";
Mss(hmm);
}
void __fastcall TForm1::Msg(AnsiString& txt)
{
ShowMessage(txt);
}
{
const AnsiString hmm = "Hello, World!";
Mss(hmm);
}
void __fastcall TForm1::Msg(AnsiString& txt)
{
ShowMessage(txt);
}
[color=red]Думаю, понятно что к чему?[/color][/QUOTE]
Не а. Глухо как в танке. :(
Цитата:
Если нет, могу объяснить.
Please :)
В общем-то, я и мой компилятор заблудились между Mss и Msg. А об Mss(NULL) и ShowMessage(*txt); вообще промолчим.
Если нет, могу объяснить.[/QUOTE]
Ну в первом примере ты впихнул нулевой указатель, намерянно ;)
Access Violation
Во втором примере ты объявил объект класса AnsiString константным и явно его проинициализировал. Если его не инициализировать, то выведется пустое окно. Вопрос вот в чем, могу ли я добиться оптимизации без объявляения const ? Я думаю что могу, достаточно передать указатель вместо объекта. Проблема может возникнуть только, если у меня многопоточное приложение и объект класса в одном из потоке будет уничтожен или изменен.
2Merlin: моя опечатка, в данном случае Mss=Msg ;)
Access Violation
[/QUOTE]
Да, конечно.
Этим я хотел показать, чем собственно передача по ссылке отличается от передачи по указателю. Ссылка не может быть нулевой, а сл-но исключаются вариант некорректного указания объекта, а сл-но можно исключить и проверку указателя на валидность, которая в принципе желательна при использовании указателя (хотя бы assert в debug-mode должен быть).
[QUOTE=SABROG]
Во втором примере ты объявил объект класса AnsiString константным и явно его проинициализировал. Если его не инициализировать, то выведется пустое окно.
[/QUOTE]
Нет. Во втором примере я показал, что константные объекты не могут быть переданы в функцию, которая принимает неконстантные объекты по ссылке. Т.о. const не только чтобы "контролировать изменяется объект или нет", но и для того, что бы функция могла принимать константные объекты.
[QUOTE=SABROG]
Вопрос вот в чем, могу ли я добиться оптимизации без объявляения const ? Я думаю что могу, достаточно передать указатель вместо объекта.
[/QUOTE]
"Оптимизация" - это понятие бессмысленное без понятия "критерий".
Оптимизация бывает по производительности (скорости выполнения), по используемому объему памяти, по размеру исполняемого файла и т.п.
Сам по себе const не приводит к перечисленным выше оптимизациям, но он приводит к большей читабельности кода, сохранению типовой безопасности и т.п., что приводит к более простому повторному использованию кода, простоте его модификации и поддержки, код становится более "помехоустойчивым". Т.о. применение const тоже приводит к оптимизации,- оптимизации процесса разработки.
[QUOTE=SABROG]
Проблема может возникнуть только, если у меня многопоточное приложение и объект класса в одном из потоке будет уничтожен или изменен.
[/QUOTE]
При совместном использовании одного ресурса разными потоками требуется синхронизация на системном уровне. Здесь никакие конструкции языка не влияют, т.е. const не поможет.
Здесь требуются системный объекты синхронизации (критические секции, мьютексы, семафоры и т.д.)
Есть различные трюки, позволяющие не использовать системных объектов синхронизации, но они опять же выходят за рамки языка.
[/quote]
Я о той оптимизации, о которой было упомянуто ранее. Критерием для нее служит скорость и расход памяти. Т.е. при передачи объекта в функцию - выделяется память под временный класс. Не знаю, скорее всего выполняется и его конструктор. А при выходе из функции - деструктор. Соответственно теряем память и процессорное время. Чтобы этого избежать - лучше передавать указатель. Однако бывают ситуации, когда надо активно работать с переданным объектом (удалять, вставлять элементы), но чтобы при выходе из функции в вызывающей функции он оставался прежним, тогда да, это оправданно.
В общем это уже к const не относится.
Для тех кто читает и не понял, чем отличается прототип:
Код:
void Func(AnsiString);
от
Код:
void Func(AnsiString *);
Пример того как при передаче параметра полностью копируется объект:
Код:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString txt = "Text";
CopyTest(txt);
ShowMessage(txt);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::CopyTest(AnsiString txt)
{
txt.Delete(1,txt.Length());
ShowMessage(txt);
}
{
AnsiString txt = "Text";
CopyTest(txt);
ShowMessage(txt);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::CopyTest(AnsiString txt)
{
txt.Delete(1,txt.Length());
ShowMessage(txt);
}
а это пример того, как он не копируется :)
Код:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString txt = "Text";
CopyTest(txt);
ShowMessage(txt);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::CopyTest(AnsiString &txt)
{
txt.Delete(1,txt.Length());
ShowMessage(txt);}
{
AnsiString txt = "Text";
CopyTest(txt);
ShowMessage(txt);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::CopyTest(AnsiString &txt)
{
txt.Delete(1,txt.Length());
ShowMessage(txt);}
Ну вот теперь понятно. Спасибо. А вот ещё интересно, func(int &i) - это только в С++ работает? В С, я так понял, если я хочу получить доступ к самому объекту, а не к его копии, придётся писать func(int *i), а потом, каждый раз обращаясь к содержимому i, постоянно использовать разадресацию?
[QUOTE=Green]P.S. очередная провокация?[/QUOTE]:D :D :D
[/QUOTE]
Вызывается однозначно.
Да, ссылки естьв С++, в С их нет.
Параметры в функцию можно передавать тремя способами: по значению, по ссылке (&) и по указателю (*). Ссылка и указатель содержат адрес объекта, разница между ними в том, что значением ссылки является значение объекта.
Ниже описаны основные способы передачи параметров функциям
Код:
Function1 (int Index)
{
}
Function2 (const int Index)
{
}
Function3 (int &Index)
{
}
Function4 (const AnsiString &Str)
{
}
Function5 (TStringList *List)
{
}
Function6 (TStringList const *List)
{
}
Function7 (const TStringList *List)
{
}
TCustomObject::Method8 () const
{
}
{
}
Function2 (const int Index)
{
}
Function3 (int &Index)
{
}
Function4 (const AnsiString &Str)
{
}
Function5 (TStringList *List)
{
}
Function6 (TStringList const *List)
{
}
Function7 (const TStringList *List)
{
}
TCustomObject::Method8 () const
{
}
1. Передаётся значение путём копирования. Изменение локальной переменной не изменит внешнюю.
2. Передаётся значение путём копирования. Изменение локальной переменной невозможно. Практически никогда не используется.
3. Передаётся ссылка на объект. Изменение локальной переменной изменит внешнюю. Нельзя передавать константные значения и выражения.
4. Передаётся константная ссылка на объект. Изменение локальной переменной изменит невозможно. Можно передавать константные значения и выражения.
5. Передаётся указатель на объект. Изменение локальной переменной на адрес другого объекта возможно. Изменение значения указываемого объекта изменит этот объект. Нельзя передавать адреса констант и выражений
6. Передаётся константный указатель на объект. Изменение локальной переменной на адрес другого объекта возможно. Изменение значения указываемого объекта изменит этот объект. Нельзя передавать адреса констант и выражений
7. Передаётся указатель на константный объект. Изменение локальной переменной на адрес другого объекта возможно. Изменение значения указываемого объекта недопустими. Можно передавать адреса констант и выражений.
8. Вызывается константный метод объекта. Данный метод не может изменять свои поля данных и вызывать неконстантные методы для самого себя. Такой метод может вызываться для константного объекта. Неконстантные методы для константных объектов вызываться не могут.
Выводы.
1. Любой метод класса, который не изменяет свои внутренние данные должен быть объявлен как константный. К сожалению, разработчики объектов VCL это правило не учли :( (см. 8)
2. Любой объект из "кучи", если не изменяется во время работы функции, должен передаваться указателем на константу (см. 7) для того, чтобы программист мог понять это по описанию функции.
3. Любой объект из стека, имеющий сложную структуру (например, struct), если не изменяется во время работы функции, должен передаваться константной ссылкой (см. 4) для ускорения работы программы.
4. Если объект передаётся неконстантной ссылкой либо указателем, значит он изменяется в процессе выполнения функции. Неконстантные ссылки используются для возвращения дополнительного результата функции.
5. Если локальное значение объекта в процессе работы функции изменяется, но это изменение не должно быть влиять на работу программы, объект передаётся путём копирования (см. 1)
Способы вызова 2 и 6 используются крайне редко, слово const здесь не несёт никакой смысловой нагрузки для основной программы, и означает только, что локальное значение переданного параметра (которое будет утеряно при выходе из функции) не может изменяться во время её работы:confused:
компилятор не пропустит этого:
Код:
int twice(int *a) {
return *a + *a;
}
main() {
const int a = 8;
return twice(&a);
}
return *a + *a;
}
main() {
const int a = 8;
return twice(&a);
}
а это пройдет:
Код:
int twice(const int *a) {
return *a + *a;
}
main() {
const int a = 8;
return twice(&a);
}
return *a + *a;
}
main() {
const int a = 8;
return twice(&a);
}
переменные не объявленные const пройдут в обоих случаях
Код:
_____Значение по адресу ---|
| ______________________|
| ___|__
ar эквивалентно *(ar + i)
& ar эквивалентно (ar + i)
| ^^^^^
| |--------------------------|
|________адрес значения--------|
| ______________________|
| ___|__
ar эквивалентно *(ar + i)
& ar эквивалентно (ar + i)
| ^^^^^
| |--------------------------|
|________адрес значения--------|
указатель на функцию типа double, с аргументом int:
double (*pf)(int);
функция, возвращающая указатель на double:
double *pf(int);
_____________________________________________
указатель на константу целого типа:
int const *ptr;
эквивалентно выражению:
const int *ptr
константа, указывающая на переманную целого типа:
int *const ptr;
НЕ эквивалентно выражению const *int ptr; <- это вообще не имеет смысла
константа, указывающая на константу целого типа:
int const *const ptr;
зквивалентно выражению:
const int *const ptr;
Параметры-указатели используются только для объектов, традиционно располагающихся в "куче", а также для простых линейных массивов.
"Func (const long double *pFloat)" имеет смысл только для массива. Для работы с одним значением лучше использовать ссылку - не надо будет каждый раз выполнять операцию обращения по адресу "*" (неуклюжее слово "разыменование" я не приемлю :))
Параметры-ссылки используются только для объектов, традиционно располагающихся в стеке.
Написание "Func (const TStringList &rList)" стилистически абсурдно.
А вот ситуация, когда передача константного значения может быть оправдана: "Func (const AnsiString Str)"
Дело в том, что в AnsiString используется хитрый алгоритм управления памятью. При операциях присваивания/копирования значений, передаётся только указатель на символьный массив другого объекта. При удалении объекта счётчик указателей на массив уменьшается. Сам же массив удаляется, когда счётчик ссылок обнуляется.
Сам же массив копируется только когда производится изменение строки, которую используют одновременно несколько объектов. const запрещает случайно вызвать функцию изменения объекта.
Что же касается изменяемых ссылок, то на их использование тоже есть ограничение. Компилятор выдаст предупреждение для следующего кода
Код:
Func (int &Value)
{
}
//.....
int a,b;
Func (a+b); //warning temporary value
{
}
//.....
int a,b;
Func (a+b); //warning temporary value
Дело в том, что результат выражения "a+b" записывается во временную неименованную переменную, которая удаляется из стека после выполнения функции. Соответственно, результат работы функции, изменённый по ссылке на временное значение, будет утерен.
А вот код "Func (c = a+b)" сработает, потому что фунция будет работать с определённым значением "c"
Аналогично не сработает следующий код
Код:
double Sin, Cos;
double Angle = 3.14;
SinCos (Angle, Sin, Cos);
double Angle = 3.14;
SinCos (Angle, Sin, Cos);
Функция SinCos оперирует с аргументами типа long double, к которому придётся привести параметры. Результаты приведения будут помещены во временные неименованые переменные. Но если для первого параметра - константной ссылке - это не страшно, то изменение прочих (неконстантных) параметров уйдёт в "никуда".
Ну и внутри функций иногда для сокращения записи использую ссылки как алиасы.
Ну и внутри функций иногда для сокращения записи использую ссылки как алиасы.
И очень зря. Ссылки как раз и созданны для упрощения их использования и более логичны в ООП чем указатели. Так что передавать в методы значения как руз лучше по ссылками, где это возможно. Хотя возможно не всегда - указатели тоже нужны.
Код:
int divide(int &a, int b) {
int result = a / b;
a = a % b;
return result;
}
int result = a / b;
a = a % b;
return result;
}
теперь, когда я вижу:
c = divide(a, b);
я привык думать что a и b не изменяются, что может стать причиной трудноуловимого бага.
Поэтому я бы объявил подобную функцию так:
Код:
int divide(int *a, int b) {
int result = a / b;
*a = a % b;
return result;
}
int result = a / b;
*a = a % b;
return result;
}
и при вызове сразу видно что изменяется а что нет:
c = divide(&a, b);
Да и потом есть еще и передача константных объектов.
На Java приходилось программировать?
В чем ошибка-то?
[QUOTE=aks]Да и потом есть еще и передача константных объектов.[/QUOTE]
Дык с константными объектами-то и проблем нет...
[QUOTE=aks]На Java приходилось программировать?[/QUOTE]
Нет, к счастью пока не приходилось :D
[/QUOTE]
Функциям надо давать говорящие имена.
Функция не должна изменять входных значений (поэтому значения лучше и передавать как константные), если она именно для этого не предназначена.
Приведенная тобой функция должна быть на самом деле двумя функциями: "результат целочисленного деления" и "остаток целочисленного деления".
[QUOTE=Svyatozar]
Код:
int divide(int *a, int b) {
int result = a / b;
*a = a % b;
return result;
}
int result = a / b;
*a = a % b;
return result;
}
[/QUOTE]
Наверное все же:
Код:
int divide(int *a, int b) {
int result = *a / b;
*a = *a % b;
return result;
}
int result = *a / b;
*a = *a % b;
return result;
}
Тогда где проверка (хотябы в режиме отладки), что указатель валиден?
[/QUOTE]
Благодарю за поправку.
Согласен что пример не идеален. Ведь дело не только в том изменяется ли значение входной переменной или только выходной, хотя конечно изменение входной переменной добавляет дополнительный слой тумана в коде. Ну и что если бы я объявил функцию так:
int divide_rem(int &remainder, int a, int b);
Все равно при вызове будет что-то такое:
a3 = divide_rem(my_rem, a1, a2);
Ну это к примеру опять же. Так вот здесь не так очевидно что происходит, то есть наглядность кода страдает. В то время как:
a3 = divide_rem(&my_rem, a1, a2);
тут сразу видно что в my_rem возвращаяется какое-то значение. Просто для этого надо использовать указатель в объявлении функции:
int divide_rem(int *remainder, int a, int b);
и соответственно изменить тело функции. Все это лишь вопрос стиля и наглядности.
int divide_rem(int &remainder, int a, int b);
Все равно при вызове будет что-то такое:
a3 = divide_rem(my_rem, a1, a2);
Ну это к примеру опять же. Так вот здесь не так очевидно что происходит, то есть наглядность кода страдает. В то время как:
a3 = divide_rem(&my_rem, a1, a2);
[/QUOTE]
Для наглядности, параметры, которые изменяются по ссылке, нужно помещать в конце списка
Код:
int divide_rem (int a, int b, int &rem);
Тогда rem стилистически и логически будет восприниматься, как результат обработки первых двух параметров. Такое правило действует для всех функций VCL.
Кроме того, при написании кода, IDE имеет привычку высвечивать типы и имена аргументов функции.
Странно, а я думал что конец списка для необязательных параметров...
[QUOTE=el scorpio]
Код:
int divide_rem (int a, int b, int &rem);
Да, но при вызове далеко не очевидно что функция вообще передает результат по ссылке!
[QUOTE=el scorpio]Тогда rem стилистически и логически будет восприниматься, как результат обработки первых двух параметров. Такое правило действует для всех функций VCL.[/QUOTE]
Можно пример? А то я не знаком с этой библиотекой.
[QUOTE=el scorpio]Кроме того, при написании кода, IDE имеет привычку высвечивать типы и имена аргументов функции.[/QUOTE]
При написании-то понятно, а при чтении?
SinCos (const long double &Angle, long double &Sin, long double &Cos)
Непонять что делает - сложно :D
А для чтения кода нужно давать изменяемым переменным понятные имена
Непонять что делает - сложно :D
А для чтения кода нужно давать изменяемым переменным понятные имена[/QUOTE]
Я не любитель жарких споров на тему стиля, но лично я бы оформил подобную функцию так:
sincos(long double alpha, long double *sin_alpha, long double *cos_alpha);
long double sin(long double alpha);
long double cos(long double alpha);
Можешь хоть всю VCL переписать под себя :). Только не забудь в каждой функции добавить проверку указателей на NULL :D
[quote=Green]
А я бы так:
long double sin(long double alpha);
long double cos(long double alpha);
[/quote]
Собственно, именно так и объявлено в "Math.h" (просьба не путать файл с "Math.hpp")
Можешь хоть всю VCL переписать под себя :). Только не забудь в каждой функции добавить проверку указателей на NULL :D[/QUOTE]
Да, каждый указатель нужно проверять на ноль. Дык специальное значение указателя - это же его большое приемущество! К примеру:
sincos(long double alpha, long double *sin_alpha = 0, long double *cos_alpha = 0) {
if(sin_alpha) {
sin_alpha = sin(alpha);
}
if(cos_alpha) {
cos_alpha = cos(alpha);
}
Допустим, мне нужен только косинус:
long double cos_alpha;
sincos(alpha, 0, &cos_alpha);
или нужен только синус:
long double sin_alpha;
sincos(alpha, &sin_alpha);
Конечно такой пустяк как sincos - это не лучший пример. Но когда у функции аргументов поболе (пусть даже в структуре) - при помощи NULL-указателя можно гибко задавать какие значения нужны, а на вычисления каких не надо тратить времени.
Да, кстати, о прозрачности массивов:
Код:
#include <math.h>
inline void SinCos(long double Angle,
long double *Sin = 0, long double *Cos = 0) {
if(Sin) {
*Sin = sin(Angle);
}
if(Cos) {
*Cos = cos(Angle);
}
}
int main() {
long double alpha[5] = {.3, 2.1, 1.0, .9, .2};
long double sinalpha[5], cosalpha[5];
for(int i = 5; --i >= 0; ) {
SinCos(*(alpha + i), sinalpha + i, cosalpha + i);
}
return 0;
}
inline void SinCos(long double Angle,
long double *Sin = 0, long double *Cos = 0) {
if(Sin) {
*Sin = sin(Angle);
}
if(Cos) {
*Cos = cos(Angle);
}
}
int main() {
long double alpha[5] = {.3, 2.1, 1.0, .9, .2};
long double sinalpha[5], cosalpha[5];
for(int i = 5; --i >= 0; ) {
SinCos(*(alpha + i), sinalpha + i, cosalpha + i);
}
return 0;
}
А со ссылочками было бы я полагаю
SinCos(*(alpha + i), *(sinalpha + i), *(cosalpha + i));
так что ли? Где тут видно что вообще что-то возвращается? Что передается а что возвращается? Сплошное недоразумение на мой взгляд...
Допустим, мне нужен только косинус:
[/QUOTE]
вызываешь функцию косинуса long double cos(long double alpha);
[QUOTE=Svyatozar]
или нужен только синус:
[/QUOTE]
вызываешь функцию синуса long double sin(long double alpha);
Сама по себе идея одной функции для получения нескольких параметров - неверная.
[QUOTE=Svyatozar]
Конечно такой пустяк как sincos - это не лучший пример. Но когда у функции аргументов поболе (пусть даже в структуре) - при помощи NULL-указателя можно гибко задавать какие значения нужны, а на вычисления каких не надо тратить времени.
[/QUOTE]
... лучше потратить время на проверку условий.
зачем?
зачем одной функцией возвращать кучу решений?
Для любителей: SinCosTgCtg(..............)
[QUOTE=Svyatozar]
А со ссылочками было бы я полагаю
SinCos(*(alpha + i), *(sinalpha + i), *(cosalpha + i));
так что ли? Где тут видно что вообще что-то возвращается? Что передается а что возвращается? Сплошное недоразумение на мой взгляд...[/QUOTE]
Хм... а индексы на что?
alpha, sinalpha, cosalpha