Нұсқағыш функция: Нұсқалар арасындағы айырмашылық

Content deleted Content added
Жаңа бетте: '''Функция көрсеткіштері''', сонымен қатар '''кіші бағдарлама көрсеткіштері''' немесе '''процедура...
(Айырмашылық жоқ)

22:39, 2020 ж. маусымның 12 кезіндегі нұсқа

Функция көрсеткіштері, сонымен қатар кіші бағдарлама көрсеткіштері немесе процедура көрсеткіші дегеніміз фунцияға бағыттайтын меңзерлер болып табылады. Ол деректің сілтемесін емес, функция көрсеткіші жадында орындалатын кодты көрсетеді. Көрсеткіш бағыттайтын нысанға сілтеме жасау, әдеттегі қарапайым функцияда шақырылатын қоңыраулар арқылы орындалады. Мұндай қоңырау сондай-ақ «жанама» қоңырау ретінде белгілі, себебі функция «тікелей»тіркелген идентификатор немесе мекен-жай арқылы емес, айнымалы арқылы шақырылады.

Функция көрсеткіштерін кодты жеңілдетуге, яғни, функцияның орындалатын уақыты негізіне сүйене отырып қолдануға болады.

Функциялардың көрсеткіштерін үшінші буынды бағдарламалау тілдерімен (PL/I, COBOL, Fortran, dBASE dBL және C сияқты) және объектілі-бағытталған бағдарламалау тілдерімен (C++ және D сияқты) қолдану ұсынылған.

Қарапайым функция көрсеткіштері

Жады ішінде функцияның  мекен-жайын қамтитын айнымалның болуы, көрсеткіштік функцияның жүзеге асуының ең қарапайым жолы болып табылады. Үшінші буынды PL/I және COBOL, сондай-ақ қазіргі заманғы Паскаль және С программа тілдерінде де функционалды көрсеткіштер дәл осылай жүзеге асырылады.

Мысал( С бағдарлама тілінде)

Келесі С бағдармала тілі  көрсеткіштік функцияның екі түрін түсіндіреді:

  • Функция 1: Екі реттік параметрді қабылдап, өзге екі ретік параметрді жауап ретінде, яғни, сантиметрді дюймге айналдыру функциясы көрсетілген.
  • Функция 2: Функция көрсеткіші тұрақты массив таңбаларын және бүтін сандарды қабылдап,  таңба көрсеткіштерін қайтарады, сонымен қатар С тілі жолдарын өңдеу функциялары тағайындалады, ол таңбаның таңбалар массивіндегі бірінші реттік көрсеткішін қайтрады.
#include <stdio.h>  /* printf үшін*/	
#include <string.h> /* strchr үшін*/

double cm_to_inches(double cm) {
	return cm / 2.54;
}

// "strchr" С тілі жолдарын өңдеудегі бөлігі (яғни, түсіндірудің қажеті жоқ)
//осы жерден көре аласыз https://en.wikipedia.org/wiki/C_string_handling#Functions

int main(void) {
	double (*func1)(double) = cm_to_inches;
	char * (*func2)(const char *, int) = strchr;
	printf("%f %s", func1(15.0), func2("Wikipedia", 'p'));
	return 0;
}

Келесі бағдарлама екі функцияның бірін (sin немесе cos) жанама шақыру үшін функцияның көрсеткішін пайдаланады (compute_sum, функцияның Римандық интеграциясының аппроксимациясын есептеу). Бағдарлама main call function compute_sum функциясына ие бола отырып, оны sin кітапханалық функциясына бірінші рет және cos функциясына екінші рет жіберу арқылы екі рет жұмыс істейді. Compute_sum функциясы, өз кезегінде, funcp функциясы көрсеткішінің аргументін бірнеше рет қайталап, шақырылатын функциямен қайтарылатын мәндерді қосып, алынған соманы қайтару арқылы екі функцияның бірін шақырады. Бұл екі сома main бойынша стандартты мәнін шығаруға жазылады.

 #include <math.h>
 #include <stdio.h>
 
 // Функция функция көрсеткішін аргумент ретінде қабылдауда
 double compute_sum(double (*funcp)(double), double lo, double hi) {
     double sum = 0.0;
 
     // '*funcp' функциясын қолдана отырып қайтарылған мәндерді қосу
     int i;
     for (i = 0; i <= 100; i++) {
         // 'funcp' көрсеткішін функцияны шақыру үшін қолдан
         double x = i / 100.0 * (hi - lo) + lo;
         double y = funcp(x);
         sum += y;
     }
    return sum / 101.0 * (hi - lo);
 }
 
 double square(double x) {
      return x * x;
 }
 
 int main(void) {
     double  sum;

     // 'sin()' стандартты кітапханалық функциясын көрсеткіш ретінде қолдан 
     sum = compute_sum(sin, 0.0, 1.0);
     printf("sum(sin): %g\n", sum);

     // 'cos()' стандартты кітапханалық функциясын көрсеткіш ретінде қолдан 
     sum = compute_sum(cos, 0.0, 1.0);
     printf("sum(cos): %g\n", sum); 

     // 'square()' стандартты  кітапханалық функциясын көрсеткіш ретінде қолдан 
     sum = compute_sum(square, 0.0, 1.0);
    printf("sum(square): %g\n", sum);

    return 0;
}

Функтор

Функтор, сондай-ақ функция нысандары функция көрсеткіштеріне өте ұқсас және бірдей мақсатта қолданылады.  Функтор – бұл функцияны шақыру сияқты  синтаксисті пайдаланатын процесстерде нысанды қолдануға мүмкіндік беретін класс түрі. Функторлар көрсеткіштерге қарағанда қуаттырақ, себебі ол өзінің жеке деректер мәндерін қамтуға және орындаушыға жабылуларды эмуляциялауға мүмкіндік беруге қабілетті. Олар қажет жағдайда  кері қоңырау функциясы ретінде пайдаланылады.

Көптеген "таза" объектілі-бағытталған тілдер функциялардың көрсеткіштерін қолдамайды. Алайда, мұндай нәрсе бір әдісті анықтайтын интерфейстерге сілтемелерді пайдалана отырып, тілдердің осы түрлерінде іске асырылуы мүмкін (функция мүшесі). C# және Visual Basic.NET сияқты командалық жолдың интерфейс тілдері  жалпы типтегі көрсеткіштерді делегаттар арқылы іске асырады.

Бірінші сыныпты функцияларды қолдайтын басқа тілдерде функциялар деректер ретінде қарастырылады және басқа функциялармен тікелей динамикалық түрде берілуі, қайтарылуы және жасалуы мүмкін, бұл функциялардың көрсеткіштерінде қажеттілігін болдырмайды.

Функция көрсеткіштерін кеңінен пайдалану кодтың заманауи процессордағы жұмысын баяулатады, себебі тармақ болжаушысы тармақ бағытын анықтауға қабілетсіз болып қалуы мүмкін (функция көрсеткішінің іске асыру кезіндегі мәніне қарай), алайда әсер әлде қайда жоғары болуы да мүмкін, себебі ол индекстелмеген кестелерді іздеу арқылы айтарлықтай жиі азайтылған.

Әдіс көрсеткіштері

C++ объектілі-бағытталған бағдарламаны қолдайды, сондықтан кластардың әдістері болуы мүмкін (әдетте функциялар-мүшелер деп аталады). Статикалық емес функция (жағдай әдісі) мүшесі, ол жұмыс істейтін объектінің көрсеткіші болатын айқын емес (this көрсеткіші) параметрге ие, сонымен функция көрсеткішінің бір бөлігі объект түрін қамтуы қажет. Содан кейін бұл әдіс белгілі класс объектісі үшін «мүшеге көрсеткіш» операторларының бірін қолданады : « .* »  немесе «*»  (сәйкесінше, нысан немесе нысан көрсеткіші).

Алайда С және C++ функция көрсеткіштері  қарапайым мекен-жай ретінде жузеге асуы мүмкін болса да, sizeof(Fx)==sizeof(void *), C++ көрсеткіштері  кейде «майлы көрсеткіштер» ретінде іске асырылады, әдетте виртуалды әдістер мен виртуалды мұрамен айналысатын қарапайым функция көрсеткішінің өлшемінен екі немесе үш есе артық.  

C++

C++ -та  С тіліндегі әдістерге қосымша, std::function стандартты кітапхана үлгісін қолдануға болады, ондағы жағдайлар функция объектісі болып табылады.

#include <iostream>
#include <functional>

static double derivative(const std::function<double(double)> &f, double x0, double eps) {
    double eps2 = eps / 2;
    double lo = x0 - eps2;
    double hi = x0 + eps2;
    return (f(hi) - f(lo)) / eps;
}

static double f(double x) {
    return x * x;
}

int main() {
    double x = 1;
    std::cout << "d/dx(x ^ 2) [@ x = " << x << "] = " << derivative(f, x, 1e-5) << std::endl;
    return 0;
}

C++ тіліндегі мүшелік функциялардың сілтемесі

Бұл C++ тың көрсеткіштерді мүшелік функциялардың класстары немесе құрылымдарымен жұмыста пайдалануын көрсетеді. Олар нысан көрсеткіші немесе this қоңырауы арқылы шақырылады. Олар тек бір класстың мүшесін(немесе туындасын) дәл сол класстың көрсеткішін қолдана отырып шақырғанда ғана қауіпсіз болып келеді. Бұл мысал мүшелік функция көрсеткішінің ұарапайымдылығы үшін typedef-ты ұсынады. Статикалық мүшелер функциясының көрсеткіштері дәстүрлі "C" стилінде орындалады, себебі бұл қоңырау үшін нысан көрсеткіші қажет емес.

#include <iostream>
using namespace std;

class Foo {

public:
   int add(int i, int j) {
       return i+j;
   }
   int mult(int i, int j) {
       return i*j;
   }
   static int negate(int i) {
       return -i;
   }
};

int bar1(int i, int j, Foo* pFoo, int(Foo::*pfn)(int,int)) {
   return (pFoo->*pfn)(i,j);
}

typedef int(Foo::*Foo_pfn)(int,int);

int bar2(int i, int j, Foo* pFoo, Foo_pfn pfn) {
   return (pFoo->*pfn)(i,j);
}

typedef int(*PFN)(int);

int bar3(int i, PFN pfn) {
   return pfn(i);
}

int main() {
   Foo foo;
   cout << "Foo::add(2,4) = " << bar1(2,4, &foo, &Foo::add) << endl;
   cout << "Foo::mult(3,5) = " << bar2(3,5, &foo, &Foo::mult) << endl;
   cout << "Foo::negate(6) = " << bar3(6, &Foo::negate) << endl;
   return 0;
}

С++ және С балама синтаксисі

Жоғарыда келтірілген C және C синтаксисі барлық оқулықтарда пайдаланылатын каноникалық болып табылады, бірақ оны оқу және түсіндіру қиын. Тіпті жоғарыда келтірілген мысалдарда typedef бұл синтаксис қолданады. Дегенмен, әрбір C және C компиляторы функциялар көрсеткіштерін жарнамалауда неғұрлым айқын және қысқа механизімді қолдайды: typedef пайдаланыңыз, бірақ анықтаудың бөлігі ретінде көрсеткішті сақтамаңыз. Шын мәнінде, бұл typedef түрін тек көрсеткішпен ғана пайдалануға болады - бірақ бұл оның көрсеткішін атап көрсетеді.

// Бұл 'F', 'char' қабылдайтын және 'int' функциясын беретін функция. Анықтама басқа жерде.
int F(char c);
// Бұл 'Fn', яғни, 'char' қабылдайтын және 'int' мәнін қайтаратын функция
typedef int Fn(char c);

// Бұл 'fn', '-Fn' сілтегішінің айнымалы мәнін анықтайды және оған 'F' мекен-жайын тағайындайды.

Fn *fn = &F;  // Ескерту:«&» талап етілмейді - бірақ оның міндетін көрсетеді. 

//Бұл 'F' сөзін 'fn' қолданып, нәтиженінде 'a' айнымалысына тағайындалады
int a = fn('A');

// Бұл «Қоңырау» функциясын анықтайды, ол «Fn» сілтегішін қабылдайтын, оны шақырады және  нәтижесін көрсетеді
int Call(Fn *fn, char c) {	
  return fn(c);
} // Call(fn, c)

// Бұл қоңыраулар «Қоңырау шалу» жұмыс істеуі, «F» өтіп және «Қоңырау шалу» нәтиже тағайындау 
int call = Call(&F, 'A');   //Тағы да, '&' қажет емес 


// ЕСКЕРТУ: қолданыстағы код негіздерін сақтау үшін, жоғарыда көрсетілген мәнерді бірінші кезекте қолдануға болады;
// онда жаңа стильді қолдана отырып түпнұсқа типті анықтауға болады. 

// Бұл 'PFn' анықтайды, яғни, Fn көрсеткішінің типін.
typedef Fn *PFn;

// 'Fn *'бар жерде 'PFn' қолданыла алады 
PFn pfn = F;
int CallP(PFn fn, char c);

C++

Бұл мысалдарда жоғарыда келтірілген анықтамалар қолданылады. Атап айтқанда, Fn үшін жоғарыда берілген анықтама көрсеткіш анықтамасында мүше функциясына пайдаланылуы мүмкін екенін ескеріңіз:

// Бұл' C', ұқсас статикалық функциялары мен функциялары бар сыныпты //анықтайды, содан кейін 'c'атымен дананы жасайды
class C {
public:
static int Static(char c);
int Member(char c);
} c; // C

// Бұл 'p', 'C' көрсеткішін анықтайды және оған 'c'адресін береді
C *p = &c;

// Бұл 'Fn' үшін 'Static' көрсеткішін белгілейді.
// "this" жоқ болғандықтан, " Fn" дұрыс түр болып табылады; және 
//"fn "- ды жоғарыда көрсетілгендей пайдалануға болады.
fn = &C::Static;

// Бұл 'm' анықтайды, ' Fn 'түрі бар мүшенің көрсеткіші - 'C', және //оған' C: Member'адресін береді.
// Сіз оны барлық көрсеткіштер сияқты оңнан солға оқи аласыз: "'m' - //бұл ' C 'класс мүшесі' FN ' типті көрсеткіш"Fn C::*m = &C::Member;

// Бұл ‘C’-да ' Member’-ді шақыру үшін m '-ды ‘ қолданады, нәтижесі //'cA'- ға беріледі
int cA = (c.*m)('A');

// Бұл ‘P’-да ' Member’-ді шақыру үшін ‘m’-ды қолданады, нәтижесі //'pA'- ға беріледі
int pA = (p->*m)('A');

// Бұл 'Ref', 'C' сілтемесін қабылдайтын функцияны, ' Fn 'және ' char' //типті 'C' көрсеткішін анықтайды, функцияны тудырады және нәтижені //қайтарады
int Ref(C &r, Fn C::*m, char c) {
   return (r.*m)(c);
} // Ref(r, m, c)

// Бұл' Ptr',' Fn ' және 'char ' типті' c 'мүше көрсеткішін анықтайды, //функцияны тудырады және нәтижені қайтарады
int Ptr(C *p, Fn C::*m, char c) {	
   return (p->*m)(c);
} // Ptr(p, m, c)

//Ескерту: қолданыстағы код базасын қолдау үшін, ең алдымен, жоғарыда //берілген анықтау мәнерін пайдалануға болады екенін ескеріңіз;
// содан кейін жаңа стилді пайдалана отырып, бастапқы түрді анықтауға //болады.
// Бұл 'FnC' анықтайды, 'Fn' типті 'C' класс мүшесі көрсеткішінің //түрін анықтайды
typedef Fn C::*FnC;

//'Fn C::*' қолданылған жерде 'FnC' қолданыла алады 
FnC fnC = &C::Member;
int RefP(C &p, FnC m, char c);

Сілтемелер

https://en.wikipedia.org/wiki/Function_pointer