Полиморфизм и указатели на функции

в 11:35, , рубрики: C, c++, ооп, полиморфизм, сортировка, указатель на функцию

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

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

Итак, пусть у нас есть массив строк (хотя никто не мешает ему быть и каким-нибудь другим массивом), который нужно сортировать. Именно сортировать, а не отсортировать. То есть планируем мы это делать регулярно и разными способами.

const int n = 15;
string cities[n] = {
	"Красноярск", "Москва", "Новосибирск", "Лондон", "Токио", 
	"Пекин", "Волгоград", "Минск", "Владивосток", "Париж", 
	"Манчестер", "Вашингтон", "Якутск", "Екатеринбург", "Омск"
};

Пусть нам требуется иметь возможность сортировки по алфавиту (вверх/вниз) и по длине строки (вверх/вниз). В других случаях это может быть по росту, по весу, по населению, по зарплате. Конечно, мы могли бы реализовать какой-нибудь метод сортировки и оттиражировать его с изменением способа сравнения. Но, как мы понимаем, возникает дублирование кода со всеми вытекающими из него последствиями.

А как же иначе быть, если мы не можем передать функцию в качестве аргумента в функцию сортировки? Или, все-таки, можем? Здесь и приходят на помощь указатели на функции. Это как раз то средство, которое позволяет работать с функциями так же, как и с переменными. Ну, или почти так же.

Итак, вот он указатель на функцию. Встречайте:

typedef bool(*CompareFunction) (int i, int j);

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

bool compareUp(int i, int j) {
	return cities[i] < cities[j];
}

bool compareDown(int i, int j) {
	return cities[i] > cities[j];
}

bool compareLengthUp(int i, int j) {
	return cities[i].length() < cities[j].length();
}

bool compareLengthDown(int i, int j) {
	return cities[i].length() > cities[j].length();
} 

Теперь помещаем их в массив:

CompareFunction compares[] = {compareUp, compareDown, compareLengthUp, compareLengthDown};

А затем создаем функцию сортировки:

void sortCities(CompareFunction compare) {
	for (int i = 0; i < n - 1; i++) {
		int j_min = i;
		for (int j = i+1; j < n; j++) {
			if (<b>compare(j, j_min)</b>) {
				j_min = j;
			}
		}
		string temp = cities[i];
		cities[i] = cities[j_min];
		cities[j_min] = temp;
	}
}

А что же там выделено жирным? Это и есть полиморфный вызов: переменной compare можно подсунуть любую функцию сравнения, и вызываемая функция будет определяться динамически, обеспечивая требуемый способ сортировки.

И, наконец, нужен какой-нибудь простенький user interface, чтобы все протестировать.

int _tmain(int argc, _TCHAR* argv[]) {
	setlocale(LC_ALL, "Russian");
	
	for (;;) {
		int choice;
		cout << "Ваш выбор: ";
		cin >> choice;
		sortCities(compares[choice]);
		printCities();
	}
	system("pause");
	return 0;
}

Теперь все прекрасно. Один программист штампует логические функции, второй — оптимизирует алгоритм сортировки, а третий — работает над юзабельностью пользовательского интерфейса. И никто никому не мешает — даже дублирование кода.

P.S. Функция printCities() виртуальная, ее реализация выпадает на долю читателя.

Автор: pestunov

Источник

Поделиться

* - обязательные к заполнению поля