1 Разное

Аргументы по умолчанию

В прототипах функций можно использовать аргументы по умолчанию:



void DrawCircle(int x=100,int y=100, int rad=200);  // 1
void DrawCircle(int x,int y=100, int rad=200);      // 2
void DrawCircle(int x,int y, int rad=200);          // 3

В этом случае, вызов функции можно производить с меньшим числом фактических параметров. Остальные параметры примут значения по умолчанию, которые и будут переданы в функцию.


Примечание 1: Значения по умолчанию можно задавать для всех аргументов (функция 1).

Примечание 2: Задавать аргументы можно только в конце списка параметров (функции 1,2).

Вызовы можно осуществлять так:

  • 1-ая функция:
    • DrawCircle();
    • DrawCircle(100);
    • DrawCircle(100,600);
    • DrawCircle(100,600,100);
  • 2-ая функция:
    • DrawCircle(100);
    • DrawCircle(100,600);
    • DrawCircle(100,600,100);
  • 3-ая функция:
    • DrawCircle(100,600);
    • DrawCircle(100,600,100);

Доступ к глобальным переменным

Переменные могут быть объявлены как внутри блока (пары скобок { }), так и вне всех блоков. В первом случае такие переменные относят к локальным, а во втором - к глобальным. Что произойдет, если имена локальной и глобальной переменных совпадут?

В языках С/С++ такой случай приводит к ''сокрытию имени'', то есть локальное имя перекрывает внешнее. В С будет невозможно обратиться к глобальной переменной по имени, если такое же имя присвоено локальной переменной. В С++ для доступа к глобальной переменной используется операция :: (4 точки).


Оператор ::

  int i=5;
  int f()
  {
     int i=10;
     i++;      // локальное i теперь равно 11
     ::i++;    // глобальное i теперь равно 6
  }

Подставляемые функции

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


Если описать функцию с ключевым словом inline, то ее тело будет помещено туда, где должен быть вызов, вместо того, чтобы помещать команды перехода.


Подставляемые функции

inline int f(int x)
{
   ....
};

2 Ссылки

  • Альтернативное имя объекта
  • Должна быть инициализирована
  • Значение ссылки нельзя изменить
  • Ни один оператор не выполняет действия над ссылкой
  • Чаще всего используются для указания аргументов функции и возвращаемых значений


int i=1;
int& r1=i;
int& r2;         // ошибка

int x = r1;      // x = 1;
r1 = 2;          // i = 2;

r1++;            // i = 3;
int* pp = &r1;   // pp указывает на i */

Инициализация



float f = 2.9;
double& dr1 = 1; // ошибка
double& dr2 = f; // ошибка
const double& cdr = 1;




void f (int val, int& ref)  {  
	val++; 
	ref++; 
}
void f2 () {
  int i = 1, j = 1;
  f (i, j);              // i=1, j=2
}

Аргументы функции



double sqrt (const double&);
void f1 (double d) {
  float f = sqrt(1);
  f = sqrt(f);
  f = sqrt(d);
}




void update (float&);
void f3 (double d, float f) {
  update(2.0f);    //ошибка
  update(f);
  update(d);       //ошибка
}

Возвращаемые значения



int& fr1(int i) {
  int local = 0;
  local+=i;
  return local;       // плохо
}

int& fr2(int i) {
  static int local_st = 0;
  local_st+=i;
  return local_st;    // хорошо
}

int* fp(int i) {
  int local = 1;
  local+=i;
  return &local;     // плохо
}

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

Перегруженные имена функций



void print(int);
void print(const char*);
void print(double);
void print(long);
void print(char);



int h(char c, int i, short s, float f) { 
  print(c);    // print(char)
  print(i);    // print(int)
  print(s);    // print(int)
  print(f);    // print(double)
  print(‘a’);  // print(char)
  print(49);   // print(int)
  print(0);    // print(int)
  print("a");  // print(const char*)
}

Возвращаемые типы

возвращаемые типы не участвуют в разрешении перегрузок




float sqrt(float);
double sqrt(double);

void f(double da, float fla)
{
  float fl = sqrt(da);  // вызов sqrt(double)
  double d = sqrt(da);  // вызов sqrt(double)
  fl = sqrt(fla);       // вызов sqrt(float)
  d  = sqrt(fla);       // вызов sqrt(float)
}

Явное разрешение



void print(char);
void print(long);
void g(int i)  {
  print(i);         // ошибка
}



void pow(int, int);
void pow(double, double);
void k(char c, float f) {
  pow (2, 2);
  pow (2.0, 2.0);
  pow (2, c);
  pow (f, 2.0);
  pow (2.0, 2);     // ошибка
}

Динамическая память

В языке С для работы с динамической памятью служит набор функций стандартной библиотеки alloc. В С++ выделение и освобождение памяти выполняется на уровне операторов языка (new, delete) и в более простом виде:


Выделение и освобождение памяти

  int *a=new int;
  int *b=new int[20];
  delete a;
  delete [] b;

Использование new для экземпляров классов влечет за собой:

  • вызов конструктора
  • выделение памяти

Использование delete для экземпляров классов влечет за собой:

  • вызов деструктора
  • освобождение памяти