İşlev işaretçisi - Function pointer

Alt program işaretçisi veya prosedür işaretçisi olarak da adlandırılan işlev işaretçisi , bir işlevi işaret eden bir işaretçidir . Bir veri değerine başvurmanın aksine, bir işlev işaretçisi bellek içindeki yürütülebilir koda işaret eder. Başvurusu kaldırıldığında referans işlevi işaretçisi yapılması sonucunda elde fonksiyonu normal bir işlev çağrısında argümanları çağrılır ve geçirilebilir. Bu tür bir çağrı, "dolaylı" çağrı olarak da bilinir, çünkü işlev, doğrudan sabit bir tanımlayıcı veya adres yerine bir değişken aracılığıyla dolaylı olarak çağrılır .

İşlev işaretçileri, çalışma zamanı değerlerine dayalı olarak yürütülecek bir işlevi seçmenin basit bir yolunu sağlayarak kodu basitleştirmek için kullanılabilir.

İşlev işaretçileri, üçüncü nesil programlama dilleri ( PL/I , COBOL , Fortran , dBASE dBL ve C gibi ) ve nesne yönelimli programlama dilleri ( C++ , C# ve D gibi ) tarafından desteklenir.

Basit işlev işaretçileri

Bir işlev (veya alt program) işaretçisinin en basit uygulaması, yürütülebilir bellek içindeki işlevin adresini içeren bir değişken gibidir . Yaşlı üçüncü kuşak dilleri gibi PL / I ve COBOL yanı gibi daha modern diller Pascal ve C genellikle bu şekilde fonksiyon işaretçileri uygulamak.

C'deki örnek

Aşağıdaki C programı, iki işlev göstericisinin kullanımını göstermektedir:

  • func1 bir çift duyarlıklı (çift) parametre alır ve başka bir çift döndürür ve santimetreyi inç'e çeviren bir işleve atanır.
  • func2 , bir tam sayının yanı sıra sabit bir karakter dizisine bir işaretçi alır ve bir karaktere bir işaretçi döndürür ve bir karakter dizisinde belirli bir karakterin ilk oluşumuna bir işaretçi döndüren bir C dize işleme işlevine atanır .
#include <stdio.h>  /* for printf */
#include <string.h> /* for strchr */

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

// "strchr" is part of the C string handling (i.e., no need for declaration)
// See 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'));
	/* prints "5.905512 pedia" */
	return 0;
}

Sonraki program, iki işlevden birini ( sinveya cos) başka bir işlevden dolaylı olarak çağırmak için bir işlev işaretçisi kullanır ( compute_sumişlevin Riemann entegrasyonunun bir yaklaşıklığını hesaplayarak ). Program, işlev mainçağrısı işlevine compute_sumiki kez sahip olarak sin, ilk kez kütüphane işlevine bir işaretçi cosve ikinci kez işlev için bir işaretçi ileterek çalışır . İşlev compute_sumsırayla, işlev işaretçi bağımsız değişkeninin başvurusunu funcpbirden çok kez kaldırarak, çağrılan işlevin döndürdüğü değerleri bir araya getirerek ve elde edilen toplamı döndürerek dolaylı olarak iki işlevden birini çağırır . İki toplam standart çıktıya ile yazılır main.

#include <math.h>
#include <stdio.h>

// Function taking a function pointer as an argument
double compute_sum(double (*funcp)(double), double lo, double hi) {
    double sum = 0.0;

    // Add values returned by the pointed-to function '*funcp'
    int i;
    for (i = 0; i <= 100; i++) {
        // Use the function pointer 'funcp' to invoke the function
        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;

    // Use standard library function 'sin()' as the pointed-to function
    sum = compute_sum(sin, 0.0, 1.0);
    printf("sum(sin): %g\n", sum);

    // Use standard library function 'cos()' as the pointed-to function
    sum = compute_sum(cos, 0.0, 1.0);
    printf("sum(cos): %g\n", sum);

    // Use user-defined function 'square()' as the pointed-to function
    sum = compute_sum(square, 0.0, 1.0);
    printf("sum(square): %g\n", sum);

    return 0;
}

işlevler

İşlevler veya işlev nesneleri, işlev işaretçilerine benzer ve benzer şekillerde kullanılabilir. Bir işlev, işlev çağrısı operatörünü uygulayan ve nesnenin bir işlev çağrısıyla aynı sözdizimini kullanan ifadelerde kullanılmasına izin veren sınıf türünde bir nesnedir . İşlevler, basit işlev işaretçilerinden daha güçlüdür, kendi veri değerlerini içerebilir ve programcının kapanışları taklit etmesine izin verir . Geri arama işlevi olarak bir üye işlevi kullanmak gerekirse, geri arama işlevleri olarak da kullanılırlar.

Birçok "saf" nesne yönelimli dil, işlev işaretleyicilerini desteklemez. Buna benzer bir şey kullanılarak olsa da, dil, bu tür uygulanabilecek referanslar için arabirimler , tek tanımlayan bir yöntem (üye işlevi). C# ve Visual Basic .NET gibi CLI dilleri , delegelerle tür açısından güvenli işlev işaretçileri uygular .

Birinci sınıf işlevleri destekleyen diğer dillerde , işlevler veri olarak kabul edilir ve işlev işaretçilerine olan ihtiyacı ortadan kaldırarak doğrudan diğer işlevler tarafından dinamik olarak geçirilebilir, döndürülebilir ve oluşturulabilir.

İşlevleri çağırmak için işlev işaretçilerinin yaygın olarak kullanılması, modern işlemcilerdeki kodun yavaşlamasına neden olabilir, çünkü dal tahmincisi nereye dallanacağını çözemeyebilir (çalışma zamanında işlev işaretçisinin değerine bağlıdır), ancak bu önemli ölçüde azaltılmış dizine eklenmemiş tablo aramalarıyla fazlasıyla telafi edildiğinden, etki abartılabilir.

Yöntem işaretçileri

C++, nesne yönelimli programlama için destek içerir , bu nedenle sınıfların yöntemleri olabilir (genellikle üye işlevler olarak adlandırılır). Statik olmayan üye işlevlerin (örnek yöntemleri), üzerinde çalıştığı nesnenin işaretçisi olan örtük bir parametresi ( bu işaretçi) vardır, bu nedenle nesnenin türü, işlev işaretçisinin türünün bir parçası olarak dahil edilmelidir. Yöntem daha sonra "işaretçiden üyeye" operatörlerinden biri kullanılarak o sınıfın bir nesnesi üzerinde kullanılır: .*veya ->*(sırasıyla bir nesne veya nesneye işaretçi için).

C ve C++'daki işlev işaretçileri basit adresler olarak uygulanabilse de, tipik olarak sizeof(Fx)==sizeof(void *), C++'daki üye işaretçiler bazen sanal ile başa çıkmak için basit bir işlev işaretçisinin iki veya üç katı büyüklüğünde "yağ işaretçileri" olarak uygulanır. yöntemler ve sanal miras .

C++'da

C++'da, C'de kullanılan yönteme ek olarak , örnekleri işlev nesneleri olan C++ standart kitaplık sınıfı şablonu std::function kullanmak da mümkündür :

#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 ++ 'daki üye işlevlere işaretçiler

C++, sınıfların veya yapıların üye işlevleriyle uğraşırken işlev işaretçilerini bu şekilde kullanır. Bunlar, bir nesne işaretçisi veya bu çağrı kullanılarak çağrılır. Yalnızca o sınıfın (veya türevlerinin) üyelerini o türden bir işaretçi kullanarak arayabileceğiniz için güvenlidirler. Bu örnek ayrıca basitlik için eklenen üyeye işaretçi işlevi için typedef kullanımını gösterir. Statik üye işlevlerine yönelik işlev işaretçileri, bu çağrı için gerekli bir nesne işaretçisi olmadığından geleneksel 'C' stilinde yapılır.

#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;
}

Alternatif C ve C ++ Sözdizimi

Yukarıda verilen C ve C ++ sözdizimi, tüm ders kitaplarında kullanılan kanonik sözdizimidir - ancak okumak ve açıklamak zordur. Yukarıdaki typedeförnekler bile bu sözdizimini kullanır. Bununla birlikte, her C ve C ++ derleyicisi beyan işlev işaretçileri için daha açık ve kısa mekanizmasını destekleyen Kullanım typedefama yok tanımının bir parçası olarak işaretçi depolamak. Bu türün typedefgerçekten kullanılabilmesinin tek yolunun bir işaretçi ile olduğunu unutmayın - ancak bu, işaretçiliğini vurgular.

C ve C ++

// This declares 'F', a function that accepts a 'char' and returns an 'int'. Definition is elsewhere.
int F(char c);

// This defines 'Fn', a type of function that accepts a 'char' and returns an 'int'.
typedef int Fn(char c);

// This defines 'fn', a variable of type pointer-to-'Fn', and assigns the address of 'F' to it.
Fn *fn = &F;      // Note '&' not required - but it highlights what is being done.

// This calls 'F' using 'fn', assigning the result to the variable 'a'
int a = fn('A');

// This defines 'Call', a function that accepts a pointer-to-'Fn', calls it, and returns the result
int Call(Fn *fn, char c) {
   return fn(c);
} // Call(fn, c)

// This calls function 'Call', passing in 'F' and assigning the result to 'call'
int call = Call(&F, 'A');   // Again, '&' is not required

// LEGACY: Note that to maintain existing code bases, the above definition style can still be used first;
// then the original type can be defined in terms of it using the new style.

// This defines 'PFn', a type of pointer-to-type-Fn.
typedef Fn *PFn;

// 'PFn' can be used wherever 'Fn *' can
PFn pfn = F;
int CallP(PFn fn, char c);

C++

Bu örnekler yukarıdaki tanımları kullanır. Özellikle, için yukarıdaki tanımın Fnişaretçiden üyeye işlev tanımlarında kullanılabileceğini unutmayın:

// This defines 'C', a class with similar static and member functions,
// and then creates an instance called 'c'
class C {
public:
static int Static(char c);
int Member(char c);
} c; // C

// This defines 'p', a pointer to 'C' and assigns the address of 'c' to it
C *p = &c;

// This assigns a pointer-to-'Static' to 'fn'.
// Since there is no 'this', 'Fn' is the correct type; and 'fn' can be used as above.
fn = &C::Static;

// This defines 'm', a pointer-to-member-of-'C' with type 'Fn',
// and assigns the address of 'C::Member' to it.
// You can read it right-to-left like all pointers:
// "'m' is a pointer to member of class 'C' of type 'Fn'"
Fn C::*m = &C::Member;

// This uses 'm' to call 'Member' in 'c', assigning the result to 'cA'
int cA = (c.*m)('A');

// This uses 'm' to call 'Member' in 'p', assigning the result to 'pA'
int pA = (p->*m)('A');

// This defines 'Ref', a function that accepts a reference-to-'C',
// a pointer-to-member-of-'C' of type 'Fn', and a 'char',
// calls the function and returns the result
int Ref(C &r, Fn C::*m, char c) {
   return (r.*m)(c);
} // Ref(r, m, c)

// This defines 'Ptr', a function that accepts a pointer-to-'C',
// a pointer-to-member-of-'C' of type 'Fn', and a 'char',
// calls the function and returns the result
int Ptr(C *p, Fn C::*m, char c) {
   return (p->*m)(c);
} // Ptr(p, m, c)

// LEGACY: Note that to maintain existing code bases, the above definition style can still be used first;
// then the original type can be defined in terms of it using the new style.

// This defines 'FnC', a type of pointer-to-member-of-class-'C' of type 'Fn'
typedef Fn C::*FnC;

// 'FnC' can be used wherever 'Fn C::*' can
FnC fnC = &C::Member;
int RefP(C &p, FnC m, char c);

Ayrıca bakınız

Referanslar

Dış bağlantılar