Среда, 08.01.2025, 20:37
Главная Регистрация RSS поиск
Приветствую Вас, Гость
Меню сайта
Категории раздела
HTML [44]
Visual C++ и MFC [21]
c++ [78]
php [19]
Javascript [15]
C# [51]
загрузки [0]
XNA [10]
создание игр с помощью xna
Наш опрос
Каким языком программирования вы увлекаетесь
Всего ответов: 2420
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Реклама
Главная » Статьи » c++

Функции с произвольным количеством и типом аргументов

Произвольное количество и тип аргументов?! Хмм... Возможно ли это?

Возможно, если в прототипе некоторой функции последним аргументом (ПОСЛЕДНИМ!!!) указать многоточие (...). Рассмотрим, например, известную вам по прошлым урокам библиотечную функцию printf. Ее прототип выглядит следующим образом:

int printf(const char *format, ...);

Такое написание означает, что функция имеет один обязательный параметр (строка) и неопределенное количество (>=0) необязательных. Казалось бы здорово! Кроме того что количество параметров у функции неограничено, эти параметры могут быть любого типа.

Однако за все приходиться платить - функции необходимо узнать сколько же параметров ей передано и какого типа каждый из них. Решить эту проблему можно несколькими способами. Например, функция printf подсчитывает количество переданных параметров с помощью подсчета знаков %, которые вы вставляете в первый параметр функции (вариант %% является исключением), а тип каждого параметра распознает по модификатору, идущему за знаком % (например, %d - это целое число). Именно поэтому "обмануть" функцию не получается: при расхождении количества знаков % и числа переданных в функцию переменных функция "вылетает", а при несовпадении типов данных отображает чушь.

Есть и другие способы. Можно, например, записывать количество передаваемых параметров в первый параметр функции (первый параметр в подавляющем большинстве случаев является обязательным для функций такого рода). Также можно условиться о том какой параметр завершает список. Например, будем считать что для функции void F(int, ...); -1000000 недопустимое число. Тогда при следующем вызове функции:

F(1, 2, 3, 4, 5, -1000000);

станет возможным определить число переданных аргументов (в данном случае их 5).

Как же перебирать аргументы внутри функции? Для этого необходимо подключить библиотеку stdarg.h, объявить переменную типа va_list (на самом деле это эквивалент char*) и воспользоваться следующими функциями:

  • void va_start( va_list arg_ptr, prev_param );

    Где в качестве первого параметра подставляется созданная вами ранее переменная типа va_list, а в качестве второго самый первый параметр функции "с многоточием" (см. выше функции printf и F). Почему так? Дело в том, что в функцию параметры передаются через специальную область памяти, называемую стеком (стек будет рассмотрен в следующем уроке), и в этой области памяти переменные идут одна за другой. То есть вызов функции va_start просто привязывает переменную типа va_list к адресу первого (явного) параметра функции. Теперь, чтобы получить следующий аргумент достаточно оттолкнуться от текущего, после чего следующий элемент становится текущим и т. д.

  • type va_arg( va_list arg_ptr, type );

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

  • void va_end( va_list arg_ptr );

    Функция va_end заканчивает перебор, сбрасывая переменную типа va_list, объявленную ранее, в NULL.

В качестве примера на изложенный здесь материал (честно говоря, "въехать" в него сразу очень непросто) вам предлагается рассматриваемый ранее проект Vector, который дополнен соответствующей фукцией (см. следующий раздел). 


Практический пример использования функций с произвольным числом аргументов

Примечание: Для облегчения чтения все добавленные в проект Vector части выделены "жирным" шрифтом.

Интерфейс класса Vector:

// Предотвращение повторной компиляции кода
// с помощью директив условной компиляции
#ifndef VECTOR_H
#define VECTOR_H

class Vector
{
 double *vect; // Вектор
 int size; // Размерность вектора
public:
 Vector(); // Конструктор по умолчанию
 explicit Vector(int); // Конструктор, позволяющий задавать
 // размерность вектора, но не являющийся
 // конструктором преобразования
 Vector(const double*, int); // Конструктор, позволяющий
 // проинициализировать вектор с помощью
 // существующего массива
 Vector(const Vector&); // Конструктор копирования
 ~Vector(); // Деструктор
 double operator ! (); // Функция, вычисляющая длину вектора
 Vector& operator ++ (); // Увеличение всех компонент вектора
 // на единицу (префикс)
 Vector operator ++ (int); // Увеличение всех компонент вектора
 // на единицу (постфикс)
 Vector& operator -- (); // Уменьшение всех компонент вектора
 // на единицу (префикс)
 Vector operator -- (int); // Уменьшение всех компонент вектора
 // на единицу (постфикс)
 Vector& operator = (const Vector&); // Перегруженный оператор присваивания
 double& operator [] (int); // Перегруженный оператор индексации
 Vector& operator + (); // Перегруженный оператор + (унарный)
 Vector operator - (); // Перегруженный оператор - (унарный)
 Vector operator + (const Vector&); // Сложение двух векторов
 Vector operator + (double); // Сложение вектора с числом
 Vector& operator += (const Vector&);// Перегруженный оператор += для 
 // сложения двух векторов
 Vector& operator += (double); // Перегруженный оператор += для 
 // сложения вектора с числом
 Vector operator - (const Vector&); // Вычитание двух векторов
 Vector operator - (double); // Вычитание числа из вектора
 Vector& operator -= (const Vector&);// Перегруженный оператор -= для 
 // вычитания двух векторов
 Vector& operator -= (double); // Перегруженный оператор -= для 
 // вычитания числа из вектора
 double operator * (const Vector&); // Умножение векторов
 Vector operator * (double); // Умножение вектора на число
 Vector& operator *= (double); // Перегруженный оператор *= для 
 // умножения вектора на число
 // Ввод вектора с клавиатуры
 friend istream& operator >> (istream&, Vector&);
 // Вывод вектора на экран
 friend ostream& operator << (ostream&, Vector&);
 int GetSize(); // Функция возвращает размерность вектора

 // Вектор с любым количеством элементов,
 // count - количество аргументов
 Vector& operator () (unsigned count, ...);
};

#endif

Реализация класса Vector:

#include <iostream.h>
#include <math.h>
#include "Vector.h"

Vector::Vector()
{ // Конструктор по умолчанию
 int n;
 cout << "Input array size:\t";
 cin >> n; // Запрос размерности вектора
 while(n <= 0) // Проверка корректности ввода
 {
 cout << "Input array size:\t";
 cin >> n;
 }
 size = n;
 vect = new double[size]; // Создание вектора заданной длины
}

Vector::Vector(int n)
{
 size = n;
 vect = new double[size]; // Создание вектора заданной длины
}

Vector::Vector(const double* v, int n)
{
 size = n;
 vect = new double[size];
 for(int i = 0; i < size; i++)// Копирование элементов переданного массива
 vect[i] = v[i]; // в компоненты вектора
}

Vector::Vector(const Vector& v)
{ // Конструктор копирования
 size = v.size;
 vect = new double[size];
 for(int i = 0; i < size; i++)
 vect[i] = v.vect[i];
}

Vector::~Vector()
{ // Деструктор
 delete [] vect;
}

Vector& Vector::operator = (const Vector& v)
{ // Перегруженный оператор присваивания
 if(&v == this) // Проверка на присваивание объекта
 return *this; // самому себе

 // Если передан другой объект, то копируем его
 delete [] vect; 
 size = v.size;
 vect = new double[size];
 for(int i = 0; i < size; i++)
 vect[i] = v.vect[i];
 return *this;
}

istream& operator >> (istream& is, Vector &v)
{ // Ввод компонент вектора с клавиатуры
 for(int i = 0; i < v.size; i++)
 {
 cout << "Input vect[" << i << "]:\t";
 is >> v.vect[i];
 }
 return is;
}

ostream& operator << (ostream& os, Vector& v)
{ // Вывод вектора на экран
 os << "Vector: (";
 for(int i = 0; i < v.size; i++)
 {
 os << v.vect[i];
 if(i == v.size - 1)
 os << ")\n";
 else
 os << ", ";
 }
 return os;
}

double Vector::operator ! ()
{ // Перегруженный оператор ! для 
 // вычисления длины вектора
 double n = 0.0;
 for(int i = 0; i < size; i++)
 n += vect[i] * vect[i];
 return sqrt(n);
}

Vector& Vector::operator ++ ()
{ // Увеличение всех компонент вектора
 // на единицу (префикс)
 for(int i = 0; i < size; i++)
 vect[i]++;
 return *this;
}

Vector& Vector::operator -- ()
{ // Уменьшение всех компонент вектора
 // на единицу (префикс)
 for(int i = 0; i < size; i++)
 vect[i]--;
 return *this;
}

Vector Vector::operator ++ (int n) // n - фиктивный параметр
{ // Увеличение всех компонент вектора
 // на единицу (постфикс)
 Vector temp(*this); // Создание временного объекта и 
 // инициализация его текущим объектом
 // (объектом, для которого вызвалась функция)
 for(int i = 0; i < size; i++)// Увеличение компонент вектора на единицу
 vect[i]++; // (для текущего объекта)
 return temp; // Возврат временного объекта
}

Vector Vector::operator -- (int n) // n - фиктивный параметр
{ // Уменьшение всех компонент вектора
 // на единицу (постфикс)
 Vector temp(*this); // Создание временного объекта и 
 // инициализация его текущим объектом
 // (объектом, для которого вызвалась функция)
 for(int i = 0; i < size; i++)// Уменьшение компонент вектора на единицу
 vect[i]--; // (для текущего объекта)
 return temp; // Возврат временного объекта
}

double& Vector::operator [] (int n)
{ // Перегруженный оператор индексации
 // для проверки выхода за границы массива
 if(n < 0) // В случае если индекс меньше нуля,
 // то возвращаем нулевой элемент массива
 {
 cout << "Index of array too small:\tuse zeroth element\n";
 return vect[0];
 }
 else if(n > size - 1) // В случае если индекс больше индекса
 // последнего элемента массива,
 // то возвращаем последний элемент массива
 {
 cout << "Index of array too big:\tuse last element\n";
 return vect[size - 1]; 
 }
 else
 return vect[n]; // Возврат заданного элемента массива
}

Vector& Vector::operator + ()
{ // Перегруженный унарный +
 return *this;
}

Vector Vector::operator - ()
{ // Перегруженный унарный -
 Vector temp(*this); // Создание временного объекта и 
 // инициализация его текущим объектом
 // (объектом, для которого вызвалась функция)
 for(int i = 0; i < size; i++)
 temp.vect[i] = -vect[i];
 return temp; // Возврат временного объекта
}

Vector Vector::operator + (const Vector& v)
{ // Сложение двух векторов с помощью 
 // перегруженного оператора бинарный +
 if(size == v.size) // Если размерности векторов совпадают
 {
 Vector temp(size); // Создание временного объекта
 for(int i = 0; i < size; i++)
 temp.vect[i] = vect[i] + v.vect[i];
 return temp; // Возврат временного объекта
 }
 else // Если размерности векторов не совпадают
 {
 cout << "Different arrays sizes !!!\nArray is truncating...\n";
 // Урезаем размерность большего вектора
 int s = (size < v.size) ? size : v.size;
 Vector temp(s); // Создание временного объекта
 for(int i = 0; i < s; i++)
 temp.vect[i] = vect[i] + v.vect[i];
 return temp; // Возврат временного объекта
 }
}

Vector Vector::operator + (double d)
{ // Сложение вектора с числом
 Vector temp(*this); // Создание временного объекта и 
 // инициализация его текущим объектом
 // (объектом, для которого вызвалась функция)
 for(int i = 0; i < size; i++)
 temp.vect[i] += d;
 return temp; // Возврат временного объекта
}

Vector& Vector::operator += (const Vector& v)
{ // Перегруженный оператор +=
 // для сложения двух векторов
 *this = *this + v; // Вызов функции this->operator=(this->operator+(v));
 return *this;
}

Vector& Vector::operator += (double d)
{ // Перегруженный оператор +=
 // для сложения вектора с числом
 *this = *this + d; // Вызов функции this->operator=(this->operator+(d));
 return *this;
}

Vector Vector::operator - (const Vector& v)
{ // Вычитание двух векторов с помощью 
 // перегруженного оператора бинарный -
 if(size == v.size) // Если размерности векторов совпадают
 {
 Vector temp(size); // Создание временного объекта
 for(int i = 0; i < size; i++)
 temp.vect[i] = vect[i] - v.vect[i];
 return temp; // Возврат временного объекта
 }
 else // Если размерности векторов не совпадают
 {
 cout << "Different arrays sizes !!!\nArray is truncating...\n";
 // Урезаем размерность большего вектора
 int s = (size < v.size) ? size : v.size;
 Vector temp(s); // Создание временного объекта
 for(int i = 0; i < s; i++)
 temp.vect[i] = vect[i] - v.vect[i];
 return temp; // Возврат временного объекта
 }
}

Vector Vector::operator - (double d)
{ // Вычитание числа из вектора
 Vector temp(*this); // Создание временного объекта и 
 // инициализация его текущим объектом
 // (объектом, для которого вызвалась функция)
 for(int i = 0; i < size; i++)
 temp.vect[i] -= d;
 return temp; // Возврат временного объекта
}

Vector& Vector::operator -= (const Vector& v)
{ // Перегруженный оператор -=
 // для вычитания двух векторов
 *this = *this - v; // Вызов функции this->operator=(this->operator-(v));
 return *this;
}

Vector& Vector::operator -= (double d)
{ // Перегруженный оператор -=
 // для вычитания числа из вектора
 *this = *this - d; // Вызов функции this->operator=(this->operator-(d));
 return *this;
}

double Vector::operator * (const Vector& v)
{ // Умножение двух векторов с помощью
 // перегруженного оператора бинарное *
 double n = 0.0;
 if(size == v.size) // Если размерности векторов совпадают
 {
 for(int i = 0; i < size; i++)
 n += vect[i] * v.vect[i]; // Вычисляем произведение
 return n; // Возвращаем произведение
 }
 else // Если размерности векторов не совпадают
 {
 cout << "Different arrays sizes !!!\nArray is truncating...\n";
 // Урезаем размерность большего вектора
 int s = (size < v.size) ? size : v.size;
 for(int i = 0; i < s; i++)
 n += vect[i] * v.vect[i]; // Вычисляем произведение
 return n; // Возвращаем произведение
 }
}

Vector Vector::operator * (double d)
{ // Умножение вектора на число
 Vector temp(*this); // Создание временного объекта и
 // инициализация его текущим объектом
 // (объектом, для которого вызвалась функция)
 for(int i = 0; i < size; i++)
 temp.vect[i] *= d;
 return temp; // Возврат временного объекта
}

Vector& Vector::operator *= (double d)
{ // Перегруженный оператор *=
 // для умножения вектора на число
 *this = *this * d; // Вызов функции this->operator=(this->operator*(d));
 return *this;
}

int Vector::GetSize()
{ // Функция, возвращающая размерность вектора
 return size;
}

#include <stdarg.h>

Vector& Vector::operator () (unsigned count, ...)
{
 // Если нет элементов
 if(count == 0)
 {
 return *this;
 }

 // Удаляем старый вектор
 delete [] vect;
 // Создаем новый вектор
 size = count;
 vect = new double[size];

 double argument;
 int i = 0;
 // Создаем список
 va_list list;
 // Инициализируем список адресом первого переданного функции параметра
 va_start(list, count);

 // Перебираем аргументы
 while(i < size) 
 {
 // Получаем следующий элемент
 // Типы данных у переданных и извлекаемых аргументов должны совпадать
 argument = va_arg(list, double /* !!! */);
 // Записываем данные в массив
 vect[i++] = argument;
 }
 // Закрываем список
 va_end(list);

 return *this;
}

Использование класса Vector:

#include <iostream.h>
#include "Vector.h"

void main()
{
 Vector v(4); // Создание вектора
 cin >> v; // Ввод компонент вектора
 cout << !--(v*5) << endl; // Вывод длины заданного вектора
  
 // Задание вектора из 10 элементов
 v(10 /* Количество аргументов */, 
 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0
 /* Все переменные типа double !!! */); 
 // Вывод заданного вектора
 cout << v << endl;
}

Категория: c++ | Добавил: slava (30.05.2011)
Просмотров: 3446 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]