SORU
5 EYLÜL 2013, PERŞEMBE


-Sipariş yüksek fonksiyon bu iki tanım arasında herhangi bir fark var mı?

Ana 4 tablolar arasında herhangi bir fark var mı? Sadece apply2(&işlev) mantıklı olduğuna inanıyorum. Ancak, tüm 4 aynı değeri döndürür.

int func(void) 
{
    return 1;
}

int apply1( int f1(void) )
{
    return f1();
}

int apply2( int (*f1) (void) ) 
{
    return f1();
}

int main() 
{
    apply1(func); 
    apply1(&func);
    apply2(func);
    apply2(&func);

    return 0;
}

CEVAP
5 EYLÜL 2013, PERŞEMBE


İlk olarak, işlev işaretçileri zor. Başka bir fonksiyonu parametre olarak bir işlev geçebilir düşünerek bazı zihin-bükme anlayış özyineleme benzer gerektirir. İlk başta alamazsın, ama sonra aniden açık beyninizde anlama kapakları gibi aydınlandın.

Ama sonra, hala C ve C parametreleri olarak görev geçme kuralları bilmek zorunda . Bu dillerde fonksiyonlar vatandaşları birinci sınıf değil, onlarla neler yapabileceğiniz pek çok kısıtlama var.

Sözdizimi

İşlev işaretçisi sözdizimini biraz çirkin. Temel anatomi [return type] (*[name])([argument list]). *name etrafında parantezgereklibir işlev işaretçisi bir işaretçi döndüren bir fonksiyon arasındaki belirsizliği ortadan kaldırmak için

// not function pointers: * not grouped to function name
int x(); // function that returns an int
int* x(); // function that returns an int*
int *x(); // also a function that returns an int*, spaces don't matter

// function pointers: * grouped to function name
int (*x)(); // pointer to a function that returns an int
int* (*x)(); // pointer to a function that returns an int*

Çürüme

Parametre olarak geçirilmesi açısından, fonksiyonlar diziler gibi davranırlar. Vefat ettiğinde, bir işaretçi içine değiştirin. Karşılaştırın:

void Foo(int bar[4]); // equivalent to: void Foo(int* bar)
void Bar(int baz()); // equivalent to: void Bar(int (*baz)())

Bu sadece fonksiyonlar ve diziler devredilemez ve olmayan copyable neden olur

int foo[4];
int bar[4] = foo; // invalid

int foo();
int bar() = foo; // invalid

Bu nedenle, fonksiyon, parametre olarak onları geçmek için tek yolu onları kopyalamak yerine kendi adresini geçmektir. (Bu diziler için tartışılabilir, ama bu işler böyledir.) Bu aslında "" parametreler denir olarak geçirilen zaman işaretçiler dönüşüyor"". çürük değerleri

Bu iki prototip uyumlu (aynı işlevi, farklı aşırı değil diyorlar), ve bu nedenle, ikisi arasında fark var

int foo(void bar());
int foo(void (*bar)());

Görsellik bir yana, bu iki bildiri arasında kesinlikle fark yoktur. Her iki işlevi, kabulişaretçibunu gibi olsun ya da olmasınçürüme. Çürüme genellikle kötü ve kafa karıştırıcı bir şey olarak kabul edilir, çünkü olsa, en geliştiriciler açıkça bir işlev işaretçisi için (ve geliştiriciler için bile çok fonksiyon türleri çürük olabilir bilmiyorum) sormak tercih eder.

Örtük Dönüştürmeler

Şimdi, parametre olarak fonksiyonlara geçirilmesi hakkında. Bu sadece çürük bir sonucudur: bir fonksiyon işaretçisi kendi türüne örtük olarak dönüştürülebilir. Bu işlev işaretçisi beklenen bir işlevi iletebilir anlamına gelir, ve derleyici için adresini alır. Bu amaçla, bu, bir kez daha aynı:

int foo();
int (*bar)() = foo; // the compiler implicitly assigns the address of foo to bar
int (*baz)() = &foo; // you explicitly assign the address of foo to baz

Bu iki açıklama birleştirir ve dört işlev çağrıları hep aynı olduğunu fark edeceksiniz. apply1 apply2 ikisiyle de aynı tür parametre (int (*)(void)) bile çok belli olmuyor apply1; ve zaman arama işlevleri ile func yerine &func, derleyici örtük olarak alır size bir adres, eşdeğer &func.


Aşağıdaki soru kapsamı dışındadır, ama önceki bölümü üzerinde durur, ve bu çok ilginç bence.

Kaynaklar [C] işlevi

Az bilinen bir gerçektir, ama aynı zamanda geçirmekreferanslardiziler ve fonksiyonlar: bu durumda, hiçbir çürüme olur. Bu gibi:

void Foo(int (&bar)[4]); // NOT equivalent to void Foo(int* bar)
void Bar(int (&baz)()); // NOT equivalent to void Bar(int (*baz)())

Bu senaryoda, işaretçi türleri ve başvuru türleri arasında örtülü dönüşüm yok çünkü Adres operatörü kullanmak yasaktır. Çürüme yenerek çürüğü genellikle kafa karıştırıcı olduğu için genelde iyi bir şey olarak görülüyor.

int baz();
Bar(baz); // valid
Bar(&baz); // INVALID

İşlev başvuruları da aynı kurallar normal başvurular gibi itaat: sadece tanım zaman ve boş olamaz. atanabilir

Typedefs

İşlev işaretçileri daha az çirkin typedef kullanarak yapabilirsiniz.

typedef int (*X)();
X func; // func is a pointer to a function that returns an int

Eğer herşey (*) kısmını alırsan daha ilgi çekici olsun

typedef int X();
X* func; // func is a function pointer
X& func; // func is a function reference [C   only]
X func; // func is a function declaration (!!)

Sonuncu durumda, 33* *beyan int func(); söyleyerek eşdeğerdir. Herkes defolup karıştırmak istemiyorsan evde bunu yapma.

decltype bir fark [C] yapar

İşlevler ve işlev işaretçileri arasında ilginç bir fark decltype ile ortaya çıkar. decltype "gelirler" expresson türü. Bu yapı içinfunction &function arasındaki fark:

int bar();
decltype(bar); // type is int ()
decltype(&bar); // type is int (*)()

Bu fark varsa std::unique_ptr bir şablon parametresi olarak yazın ki, geçmek istiyorsanız, bu özellikle önemlidir.

std::unique_ptr<void, decltype(free)> foo; // INVALID
std::unique_ptr<void, decltype(&free)> foo; // valid

İlk unique_ptr örnek olarak alan bir fonksiyon oluşturmaya çünkü geçersiz.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • El SalvaLobo

    El SalvaLobo

    10 Temmuz 2006
  • Jejoab

    Jejoab

    4 NİSAN 2008
  • Kevin Bruckert

    Kevin Brucke

    30 Aralık 2006