Пятница, 29.03.2024, 13:01
Приветствую Вас Гость | RSS

Кузница миров

Меню сайта
Категории раздела
Мои статьи [2]
Курс : "Основы С++ для начинающих программистов игр." [25]
WindMill Engine [3]
XNA4 RPG [0]
Перевод туториалов но созданию RPG на C# c XNA4.
C# & Leadwerks [5]
Программирование Leadwerks Engine на языке С# с помощью врапера Le.NET.

Каталог статей и уроков

Главная » Статьи » Курс : "Основы С++ для начинающих программистов игр."

5.3. Обширный функционал
5.3. Обширный функционал


Вообще-то, уже можно было бы приступить к написанию игры. Для использования функций вы знаете все что нужно. Но все же, пока вы серьезно настроены на учебу, я расскажу вам о функциях поподробнее. Я расскажу вам много интересного.
Если же вам невтерпеж, можете сразу переходить к шестому уроку. Вот только в процессе вам все равно придется возвращатся, и перечитывать пропущенное.

Перегрузка функций


Идя по свежим следам, вновь вернемся к именам. Именам функций.
Имя, которое используем мы и имя, которым функция представлена в программе, различны. В отличии от нас, программа также учитывает количество и тип аргументов а также тип возвращаемого значения. Все это вместе называют полным именем функции, ее сигнатурой.
Пример:
Code

int func();
char func();
int func(int);
int func(char);
char func(int,int);
char func(int,float);

Все это разные функции.
Возможность создавать функции с одинаковыми именами называется перегрузкой функций.
Может возникнуть вопрос, зачем же эта перегрузка нужна. Зачем сознательно вносить такую путаницу в программу. Оказывается для того, чтобы эти самые программы упростить. Понять это поможет следующий пример:
Code

int sum(int a, int b)
{
   return a+b;
}
float sum(float a, float b)
{
   return a+b;
}

void main()
{
   cout<<sum(10,20);
   cout<<sum(10.5,20.7);
}

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

Необходимо отметить, что указанный выше пример работать в Microsoft Visual Studio 2008 не будет. Компиляция завершится следующей ошибкой:

error C2668: sum: неоднозначный вызов перегруженной функции

Поскольку компилятор сам неплохо умеет преобразовывать типы, новые стандарты требуют, чтобы во избежание таких вот ситуаций, программист явно указывал тип данных.
Вот так:
Code

cout<<sum(10.5f,20.7f);


или так:
Code

cout<<sum((float)10.5,(float)20.7);


Значение аргументов по умолчанию.


И еще немного об именах.
В С++ есть возможность присваивать используемым в функциях аргументам значение по умолчанию. Делается это очень просто. Достаточно выполнить присваивание в момент объявления функции:
Code

int func(int i=50);


Здесь аргументу, представленному переменной int i, по умолчанию присвоено 50. Оно будет использоваться, если при вызове не передавать функции никакого значения.
Пример:
Code

#include <iostream>
using namespace std;

void func(int i=50);    // обьявление с заданием аргумента по умолчанию

void main()
{
   func();    // первый вызов
   func(111);    // второй вызов
   cin.get();
}

void func(int i)
{
   cout<<i<<"\n";
}

Здесь при первом вызове функции не передается никакое значение, поэтому используется значение по умолчанию. При втором вызове используется 111.

Конечно же у функции может быть больше одного аргумента.
Code

void func(int arg1, int arg2, int arg3);


Присвоить значение по умолчанию аргументу arg2, можно только в том случае, если значение по умолчанию уже есть у arg3.
Так делать нельзя:
Code

void func(int arg1, int arg2 = 125, int arg3);

Можно только так:
Code

void func(int arg1, int arg2 = 125, int arg3 = 0);

Если объявление функции не используется, то аргументы по умолчанию задаются в ее реализации:
Code

#include <iostream>
using namespace std;

void func(int i=50)    //  задание аргумента по умолчанию
{
   cout<<i<<"\n";
}

void main()
{
   func();    // первый вызов
   func(111);    // второй вызов
   cin.get();
}


Встраиваемые функции


Использование функций имеет один маленький недостаток. На вызов функции тратится некоторое время. Происходит это потому, что под код функции резервируется отдельное место в памяти. По ходу выполнения программы, при вызове функции программе приходится переходить к этому месту, а затем возвращаться обратно. Естественно что этот переход не происходит мгновенно, и хорошо если она вызывается всего раз, а вот если 100, значит и переходов будет столько же, и потраченного времени в сто раз больше.
Получается что получить большую скорость работы программы можно, вписывая код функции в место вызова. Это еще бы было нормально, если бы функция вызывалась всего пару раз, а вот если опять сотню. Это ж получится такой объем кода, что сам черт в нем не разберется.
Чтобы обойти все эти проблемы, можно использовать оператор inline.
Синтаксис следующий:
Code

inline Тип_возвращаемого_значения  имя_функции(список агрументов);

Пример:
Code

inline int sum();

Этот оператор заставляет компилятор встраивать код функции в место ее вызова. Как говорится, и волки сыты и овцы целы, то есть и код небольшой и легко читаем, и программа работает быстро.
Но все же есть одно но. Если такая функция вызывается 100 раз, то при компиляцию в программу будет вставлено 100 ее экземпляров. За увеличение скорости придется заплатить увеличением программного кода и занимаем местом в памяти. В результате никакого выигрыша может и не быть. Поэтому нужно руководствоваться следующим правилом: Если функция содержит всего две-три строки кода, то ее можно, и даже нужно, делать встраиваемой. Если же функция большая, да к тому же используется много раз, то делать ее встраиваемой нельзя.

Статические переменные.


Допустим, что нам зачем-то нужно посчитать, сколько раз конкретная функция вызывалась в программе. Первый пришедший на ум способ сделать это – создать отдельную глобальную переменную и в коде функции увеличивать ее на единицу.
Вот так:
Code

#include <iostream>
using namespace std;

int r=0;    // глобальная переменная-счетчик

inline int sum(int a, int b)
{
   r++;    //использование глобальной переменной
   return a+b;
}

void main()
{
   for(int i =0;i<50;i++)
   {
    sum(10,20);
   }
   cout<<r;    // результат подсчета
   cin.get();
}

Есть у подобного способа один недостаток. Допустим, вы скопировали вашу функцию в другую программу, или вообще дали своему другу. А в этой другой программе, ну или у друга, нет специально объявленной глобальной переменной. Программа работать не будет и хорошо, если вы вспомните почему.
Естественно, способ решить подобную проблему есть. Это оператор - static.
Объявив с его помощью переменную внутри функции
Code

int sum(int a, int b)
{
   static int r=0;    // статическая переменная-счетчик
   r++;   
   return a+b;
}

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

Рекурсивные функции


Может быть то, что я сейчас скажу, покажется вам бредом, но знайте, функция способна вызывать сама себя.
Code

int func()
{
func();
}

Называется эта возможность рекурсией. Рекурсия может быть прямая и косвенная.
Прямая – когда функция вызывает сама себя, косвенная – когда функция вызывает другую функцию, которая в свою очередь вызывает первую. Кстати, это уже знакомая ситуация:
Code

void b();

void a()
{
   b();
}
...
void b()
{
   a();
}


Применяется рекурсия в разных случаях, когда например нужно выполнить ее для какого то значения, а затем эта же функция применяется к полученному результату.
Для рекурсивных функций является обязательным наличие условия, способного прекратить рекурсию.
Небольшое откровение. Я не люблю рекурсию, особенно прямую. Нет такой ситуации, когда бы без нее нельзя было обойтись. К тому же множественный вызов функций, передача аргументов расходуют дополнительное время и память. Но все же есть одно преимущество - использование рекурсии позволяет сделать код более компактным.
Специально приводить пример рекурсии я не буду.
Категория: Курс : "Основы С++ для начинающих программистов игр." | Добавил: nilrem (11.05.2012)
Просмотров: 2070 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0