C ++ temsilcisi nedir?


153

C ++ 'da bir temsilcinin genel fikri nedir? Nedir, nasıl kullanılır ve ne için kullanılır?

Onları ilk önce 'kara kutu' yoluyla öğrenmek isterdim, ancak bunların içgüdüsü hakkında biraz bilgi de harika olurdu.

Bu, en saf veya en temiz haliyle C ++ değil, ancak çalıştığım kod tabanında bunlara bolca sahip olduğunu fark ettim. Onları yeterince anlamayı umuyorum, böylece onları kullanabilirim ve korkunç iç içe geçmiş şablon korkunçluğuna dalmak zorunda kalmayabilirim.

Bu iki Kod Projesi makalesi ne demek istediğimi açıklıyor, ancak özellikle kısa ve öz değil:


4
.NET altında yönetilen C ++ 'dan mı bahsediyorsunuz?
Sergey Kalinichenko


5
delegatec ++ dilinde yaygın bir ad değildir. Soruya okuduğunuz bağlamı dahil etmek için bazı bilgiler eklemelisiniz. Kalıp ortak olsa da , genel olarak temsilci hakkında konuşursanız veya C ++ CLI veya belirli bir temsilci uygulamasına sahip başka bir kitaplık bağlamında yanıtlar farklı olabilir .
David Rodríguez - dribeas

6
2 yıl bekleyin
?;

6
Hala bekliyor ...
Grimm The Opiner

Yanıtlar:


180

C ++ 'da delege elde etmek için inanılmaz sayıda seçeneğiniz var. İşte aklıma gelenler.


Seçenek 1: işlevler:

Uygulanarak bir işlev nesnesi oluşturulabilir operator()

struct Functor
{
     // Normal class/struct members

     int operator()(double d) // Arbitrary return types and parameter list
     {
          return (int) d + 1;
     }
};

// Use:
Functor f;
int i = f(3.14);

Seçenek 2: lambda ifadeleri ( yalnızca C ++ 11 )

// Syntax is roughly: [capture](parameter list) -> return type {block}
// Some shortcuts exist
auto func = [](int i) -> double { return 2*i/1.15; };
double d = func(1);

Seçenek 3: işlev işaretçileri

int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);

Seçenek 4: Üye işlevlere işaretçi (en hızlı çözüm)

Bkz. Hızlı C ++ Temsilcisi ( Kod Projesinde ).

struct DelegateList
{
     int f1(double d) { }
     int f2(double d) { }
};

typedef int (DelegateList::* DelegateType)(double d);

DelegateType d = &DelegateList::f1;
DelegateList list;
int a = (list.*d)(3.14);

Seçenek 5: std :: function

(veya boost::functionstandart kitaplığınız desteklemiyorsa). Daha yavaştır, ancak en esnek olanıdır.

#include <functional>
std::function<int(double)> f = [can be set to about anything in this answer]
// Usually more useful as a parameter to another functions

Seçenek 6: bağlama ( std :: bind kullanarak )

Bazı parametreleri önceden ayarlamaya izin verir, örneğin bir üye işlevini çağırmak için uygundur.

struct MyClass
{
    int DoStuff(double d); // actually a DoStuff(MyClass* this, double d)
};

std::function<int(double d)> f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1);
// auto f = std::bind(...); in C++11

Seçenek 7: şablonlar

Bağımsız değişken listesiyle eşleştiği sürece her şeyi kabul edin.

template <class FunctionT>
int DoSomething(FunctionT func)
{
    return func(3.14);
}

2
Harika liste, +1. Bununla birlikte, burada yalnızca iki delege sayılır - lambdaları ve geri dönen nesneyi yakalamak std::bindve her ikisi de gerçekten aynı şeyi yapar, ancak lambdalar farklı argüman türlerini kabul edebilme anlamında polimorfik değildir.
Xeo

1
@JN: Neden işlev işaretçileri kullanmamayı öneriyorsunuz, ancak üye yöntem işaretlerini kullanmakta sorun yok gibi görünüyor? Sadece aynılar!
Matthieu M.

1
@MatthieuM. : doğru tespit. İşlev işaretçilerinin miras olduğunu düşünüyordum, ama bu muhtemelen sadece kişisel zevkim.
JN

2
@SirYakalot Bir işlev gibi davranan, ancak aynı anda durumu tutabilen ve diğer herhangi bir değişken gibi manipüle edilebilen bir şey. Örneğin bir kullanım, iki parametre alan bir işlevi alıp 1. parametreyi, tek bir parametre ile ( bindinglistemde) yeni bir işlev oluşturan belirli bir değere sahip olmaya zorlamaktır . Bunu işlev işaretçileriyle başaramazsınız.
JN

3
C ++ 'da mevcut değiller. Dolayısıyla liste.
JN

38

Temsilci, bir nesne örneğine bir işaretçi veya başvuruyu saran bir sınıftır, bu nesnenin sınıfının bir üye yönteminin o nesne örneğinde çağrılması ve bu çağrıyı tetiklemek için bir yöntem sağlar.

İşte bir örnek:

template <class T>
class CCallback
{
public:
    typedef void (T::*fn)( int anArg );

    CCallback(T& trg, fn op)
        : m_rTarget(trg)
        , m_Operation(op)
    {
    }

    void Execute( int in )
    {
        (m_rTarget.*m_Operation)( in );
    }

private:

    CCallback();
    CCallback( const CCallback& );

    T& m_rTarget;
    fn m_Operation;

};

class A
{
public:
    virtual void Fn( int i )
    {
    }
};


int main( int /*argc*/, char * /*argv*/ )
{
    A a;
    CCallback<A> cbk( a, &A::Fn );
    cbk.Execute( 3 );
}

aramayı tetiklemek için bir yöntem sağlayan nedir? delege? Nasıl? işlev işaretçisi?
SirYakalot

Temsilci sınıfı Execute(), temsilcinin sarmaladığı nesnede işlev çağrısını tetikleyen bir yöntem sunacaktır .
Grimm The Opiner

4
Execute yerine, sizin durumunuzda çağrı operatörünü geçersiz kılmanızı şiddetle tavsiye ederim - void CCallback :: operator () (int). Bunun nedeni genel programlamadır, çağrılabilir nesnelerin bir işlev gibi çağrılması beklenir. o.Execute (5) uyumsuz olacaktır, ancak o (5) çağrılabilir bir şablon argümanı olarak güzelce uyacaktır. Bu sınıf da genelleştirilebilir, ancak kısalık için basit tuttuğunuzu varsayıyorum (bu iyi bir şey). O nitpick dışında, bu çok kullanışlı bir sınıf.
David Peterson

18

C ++ temsilci uygulamalarına duyulan ihtiyaç, C ++ topluluğu için uzun süreli bir utanç kaynağıdır. Her C ++ programcısı bunlara sahip olmayı çok ister, bu nedenle şu gerçeklere rağmen sonunda bunları kullanırlar:

  1. std::function() yığın işlemlerini kullanır (ve ciddi gömülü programlama için erişilemez).

  2. Diğer tüm uygulamalar, daha büyük veya daha düşük derecelere taşınabilirlik veya standart uygunluk konusunda taviz verir (lütfen burada ve kod projesinde çeşitli delege uygulamalarını inceleyerek doğrulayın). Wild reinterpret_casts kullanmayan bir uygulama, kullanıcı tarafından iletilenle aynı boyutta işlev işaretçileri üretmesi ümit edilen Nested sınıf "prototipleri", ilk ileri bildirme, sonra typedef ve sonra tekrar bildirme gibi derleyici hileleri, bu sefer başka bir sınıftan veya benzer gölgeli tekniklerden miras alınır. Bunu inşa eden uygulayıcılar için büyük bir başarı olsa da, yine de C ++ 'nın nasıl geliştiğinin kanıtıdır.

  3. Şu anda 3'ten fazla C ++ standart revizyonunun, delegelerin düzgün bir şekilde ele alınmadığı nadiren belirtiliyor. (Veya doğrudan temsilci uygulamalarına izin veren dil özelliklerinin olmaması.)

  4. C ++ 11 lambda işlevlerinin standart tarafından tanımlanma şekliyle (her lambda anonim, farklı türe sahiptir), durum yalnızca bazı kullanım durumlarında iyileşmiştir. Ancak (DLL) kitaplık API'lerinde temsilcilerin kullanılması durumunda, lambdalar tek başına hala kullanılamaz. Buradaki yaygın teknik, önce lambda'yı bir std :: function içine paketlemek ve sonra API üzerinden geçirmektir.


Elbert Mai'nin, başka yerlerde görülen diğer fikirlere dayalı olarak C ++ 11'deki genel geri aramalarının bir versiyonunu yaptım ve 2) 'de alıntı yaptığınız sorunların çoğunu temizliyor gibi görünüyor. Benim için geriye kalan tek sorun, delegeleri oluşturmak için istemci kodunda bir makro kullanmam. Bundan muhtemelen henüz çözmediğim sihirli bir şablon yolu vardır.
kert

3
Gömülü sistemde heap olmadan std :: function kullanıyorum ve hala çalışıyor.
prasad

1
std::functionher zaman dinamik olarak tahsis etmez
Orbit'teki Hafiflik Yarışları

4
Bu cevap, gerçek bir cevaptan çok bir ranttır
Hafiflik Yarışları Orbit

8

Çok basit bir şekilde, bir temsilci, bir işlev işaretçisinin nasıl çalıştığına dair işlevsellik sağlar. C ++ 'da işlev işaretçilerinin birçok sınırlaması vardır. Bir temsilci, istediğiniz şekilde çalışan bir şablon sınıfı işlev işaretçi türü bir şey oluşturmak için bazı perde arkası şablon boşluğunu kullanır.

yani - onları belirli bir işlevi gösterecek şekilde ayarlayabilir ve istediğiniz zaman ve istediğiniz yerde onları dolaştırabilir ve çağırabilirsiniz.

Burada çok güzel örnekler var:


4

C ++ 'da temsilciler için burada aksi belirtilmeyen bir seçenek, bunu bir ptr işlevi ve bir bağlam bağımsız değişkeni kullanarak C stili yapmaktır. Bu, muhtemelen bu soruyu soran birçok kişinin kaçınmaya çalıştığı modelin aynısıdır. Ancak model taşınabilir, verimli ve gömülü ve çekirdek kodunda kullanılabilir.

class SomeClass
{
    in someMember;
    int SomeFunc( int);

    static void EventFunc( void* this__, int a, int b, int c)
    {
        SomeClass* this_ = static_cast< SomeClass*>( this__);

        this_->SomeFunc( a );
        this_->someMember = b + c;
    }
};

void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext);

    ...
    SomeClass* someObject = new SomeObject();
    ...
    ScheduleEvent( SomeClass::EventFunc, someObject);
    ...

1

Standart C ++ 'daki bir işlev nesnesinin Windows Çalışma Zamanı eşdeğeri. Tüm fonksiyon bir parametre olarak kullanılabilir (aslında bu bir fonksiyon göstergesidir). Çoğunlukla olaylarla bağlantılı olarak kullanılır. Temsilci, olay işleyicilerinin çok yerine getirdiği bir sözleşmeyi temsil eder. Bir işlev işaretçisinin nasıl çalışabileceğini kolaylaştırır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.