C ++ 'da işaretçilere başvuruları iletme


130

Anlayabildiğim kadarıyla, C ++ 'da bir işaretçiye bir başvuru göndermeme izin verilmemesi için hiçbir neden yok. Ancak, bunu yapma girişimlerim başarısız oluyor ve neden olduğuna dair hiçbir fikrim yok.

Yaptığım şey bu:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

Ve şu hatayı alıyorum:

parametre 1 'std :: string *' den 'std :: string * &' e dönüştürülemiyor

Yanıtlar:


126

İşleviniz, anonim bir dizge işaretçisi değil, çağıran kapsamdaki gerçek bir dizge işaretçisine bir başvuru bekler. Böylece:

string s;
string* _s = &s;
myfunc(_s);

iyi derlenmeli.

Ancak, bu yalnızca işleve ilettiğiniz işaretçiyi değiştirmek istiyorsanız yararlıdır. Dizenin kendisini değiştirmeyi düşünüyorsanız, Sake'nin önerdiği gibi dizeye bir başvuru kullanmalısınız. Bunu akılda tutarak, derleyicinin orijinal kodunuzdan neden şikayet ettiği daha açık olmalıdır. Kodunuzda işaretçi 'anında' yaratılır, bu işaretçiyi değiştirmenin hiçbir sonucu olmaz ve amaçlanan bu değildir. Referans fikri (bir işaretçiye karşı), bir referansın her zaman gerçek bir nesneyi işaret etmesidir.


86

Sorun, referansa bir geçici bağlamaya çalışmanızdır; C ++, referans olmadığı sürece izin vermez const.

Böylece aşağıdakilerden birini yapabilirsiniz:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}


void myfunc2(string* const& val)
{
    // Do stuff to the string pointer
}

int main()
// sometime later 
{
    // ...
    string s;
    string* ps = &s;

    myfunc( ps);   // OK because ps is not a temporary
    myfunc2( &s);  // OK because the parameter is a const&
    // ...

    return 0;
}

Yazıyı örneğin 2 numaralı örnekte Bay Burr'un yaptığı gibi yazmak özellikle öğreticidir - string* const& valörneğin const string* &val. Benim gözümde bu açıkça bir göstericiye, dizgeye sabit bir referanstır . Bu kesin açıklamayı elde etmek için beyanname T const&yerine kişisel olarak yazmayı tercih ediyorum const T&.
fish2000

1
Nit pick (bir eleştiri değil): Bana göre, bind a temporary to the referencebu cevapta ifade @Chris'den daha net reference to an actual string pointer in the calling scope, not an anonymous string pointer. Ne olursa olsun, benim açımdan her ikisi de muhtemelen doğru.
kevinarpe

1
@ fish2000 Sadece bu değil, const string*&ve string* const&aslında farklı türleri vardır. Birincisi a'ya constreferans değildir const string*, ikincisi ise a'ya constreferanstır string*.
Justin Time - Monica'yı Yeniden

@ "prefering" dikkat fish2000 T const&için const T&- @Justin Zaman işaret ettiği gibi, farklı tipleri vardır. Bazen hiçbir sonucu olmayabilir, ancak diğer durumlarda, tıpkı tercih intetmek gibi, birini veya diğerini tercih etmek kesinlikle kritik olacaktır double.
omatai

3
@ fish2000 - biri "burada değiştiremeyeceğiniz bir şeye değiştirebileceğiniz bir referans var" diyor, diğeri ise "değiştirebileceğiniz bir şeye değiştiremeyeceğiniz bir referans" diyor. Verilen bu örnekte, ikisini birbiriyle değiştiremezsiniz.
omatai

10

Şununla değiştirin:

  std::string s;
  std::string* pS = &s;
  myfunc(pS);

DÜZENLE:

Bu çağrılır ref-to-pointerve işleve referans olarak geçici adresi iletemezsiniz. (olmadığı sürece const reference).

Yine de, gösterdim std::string* pS = &s;(yerel bir değişkene işaretçi), bunun tipik kullanımı şudur: aranan ucun işaret ettiği nesneyi değil, işaretçinin kendisini değiştirmesini istediğinizde. Örneğin, belleği ayıran ve bağımsız değişkenine tahsis ettiği bellek bloğunun adresini atayan bir işlev, bir işaretçiye veya işaretçiye bir göstericiye başvurmalıdır:

void myfunc(string*& val)
{
//val is valid even after function call
   val = new std::string("Test");

}

5

&s dizgeye geçici işaretçi üretir ve geçici nesneye başvuru yapamazsınız.


4
Bu tam olarak doğru değil - geçici const'a bir atıfta bulunabilirsiniz
1800 BİLGİ

3

Deneyin:

void myfunc(string& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(s);
    // ...
}

veya

void myfunc(string* val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

Yaptığım şey için, işaretçinin yanı sıra işaretçinin adresine de ihtiyacım var. İşaretçiyi değere göre geçirmek istemiyorum.
Alex 05

Hala neyi başarmaya çalıştığını anlamadım. "Dizge s" nin "göstericisinin adresi" olamaz, çünkü "dizge s" bir işaretçi değildir.
Sake

@Alex: Sanırım dizenin sadece içeriğinin aynı olup olmadığını değil, sakladığınız bir başkasıyla tamamen aynı olup olmadığını tespit etmeniz gerekiyor. Bu durumda, bir referans için operatörün adresini kullanabileceğinizi ve size referans verilen nesnenin adresini alacağını unutmayın: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas

3

DÜZENLEME: Bazılarını denedim ve bir şeyin düşündüğümden biraz daha ince olduğunu keşfettim. İşte şimdi doğru bir cevap olduğunu düşündüğüm şey.

&sbir değer değildir, bu nedenle referansın türü referans olmadıkça ona referans oluşturamazsınız const. Yani örneğin yapamazsınız

string * &r = &s;

ama yapabilirsin

string * const &r = &s;

İşlev başlığına benzer bir bildirim koyarsanız, çalışacaktır.

void myfunc(string * const &a) { ... }

Geçiciler denen başka bir konu daha var. Kural, geçici bir referansı ancak öyleyse alabilmenizdir const. Bu durumda, & s'nin geçici olduğu ve bu nedenle beyan edilmesi gerektiği iddia edilebilir.const fonksiyon prototipinde . Pratik açıdan bakıldığında, bu durumda hiçbir fark yaratmaz. (Bu ya bir r değeri ya da bir geçici. Her iki durumda da aynı kural geçerlidir.) Bununla birlikte, kesin konuşmak gerekirse, bunun geçici değil, bir r değeri olduğunu düşünüyorum. İkisini birbirinden ayırmanın bir yolu var mı merak ediyorum. (Belki de basitçe, tüm geçicilerin r değerleri olduğu ve tüm l olmayan değerlerin geçici olduğu tanımlanmıştır. Standart konusunda bir uzman değilim.)

Bununla birlikte, sorununuz muhtemelen daha yüksek bir seviyededir. Neden adresine bir referans istiyorsun s? Bir işaretçiye başvuru istiyorsanız s, aşağıdaki gibi bir işaretçi tanımlamanız gerekir.

string *p = &s;
myfunc(p);

Bir referans sveya bir işaretçi istiyorsanız s, basit olanı yapın.


1

Kök kasası haricinde silinmiş bir ikili ağaçtaki tüm işaretçileri yapmak için bir işaretçiye bir başvuru yaptım. İşaretçiyi güvenli hale getirmek için onu 0'a ayarlamamız gerekiyor. Ağacı silen (sadece kökü tutan) işlevi, ilk olarak kökü (bu gösterici) kullandığım için bir işaretçiye ref kabul etmesini sağlayamadım. sola ve sağa hareket etmek için giriş.

void BinTree::safe_tree(BinTree * &vertex ) {
    if ( vertex!=0 ) {  // base case
        safe_tree(vertex->left);    // left subtree.
            safe_tree(vertex->right);   //  right subtree.
          // delete vertex;  // using this delete causes an error, since they were deleted on the fly using inorder_LVR. If inorder_LVR does not perform delete to the nodes, then, use delete vertex;
        vertex=0;  // making a safe pointer
    }
} // end in

Alt satırda, biçimsel parametre (bu) gösterici olduğunda bir işaretçiye başvuru geçersizdir.


1

C ++ 11 ve rvalue referanslarına hoş geldiniz:

#include <cassert>
#include <string>

using std::string;

void myfunc(string*&& val)
{
    assert(&val);
    assert(val);
    assert(val->c_str());
    // Do stuff to the string pointer
}

// sometime later 
int main () {
    // ...
    string s;
    myfunc(&s);
    // ...
}

Artık valdizenin adresi olan göstericinin değerine (ile başvurulur ) erişebilirsiniz .

İşaretçiyi değiştirebilirsiniz ve kimse umursamaz. Bu, ilk etapta bir değerin ne olduğunun bir yönüdür.

Dikkatli olun: İşaretçinin değeri yalnızca myfunc()dönene kadar geçerlidir . Sonunda, bu geçici.


-1

İşaretçilerin referanslarını geçmenin mümkün olduğunu biliyorum, bunu geçen hafta yaptım, ancak kodunuz şu anda beynime doğru göründüğü için sözdiziminin ne olduğunu hatırlayamıyorum. Ancak başka bir seçenek de işaretçi işaretçileri kullanmaktır:

Myfunc(String** s)

-8

myfunc ("string * & val") bunun kendisi bir anlam ifade etmiyor. "string * & val", "string val" anlamına gelir, * ve & birbirini iptal eder. Son olarak bir dizge değişkeni bir işleve ("dizge val") geçirilemez. Diğer veri türlerinin işaretçi veya başvuru olarak geçmesi gerektiğinden, bir işleve yalnızca temel veri türleri aktarılabilir. Bir işleve ya string & val ya da string * val değerine sahip olabilirsiniz.


4
Her seviyede yanlış. Lütfen var bildirimi söz diziminizi yenileyin.
Ari
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.