top of page

C++ Dilinde Referans ve Değer Kategorileri(Value Category)

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • May 12, 2023
  • 11 min read

Updated: May 5, 2024

C programlama dilinde bir nesnenin bir fonksiyona gönderilebilmesi için nesnenin adresi gönderilir. Bir fonksiyonun kendisini çağıran fonksiyona bir nesne iletebilmesi için nesnenin adresini döndürmek gerekmektedir.

Referans(reference), bir nesnenin yerine geçen isimdir. Mevcut değişken için alternatif haline gelir diyebiliriz. "&" operatörü ile değişken reference ifadesi olarak belirtilmektedir. Pointer benzeri bir yapıdır ancak nesne yönelimli programlamaya(OOP) uygunluk için daha esnek bir yapıda sunulmaktadır.

Referansa ait bazı kuralları maddeleyecek olursak ;

  • Referans olan bir değişkene ilk değer vermek zorunludur.

  • Reference kendisi const olarak hayata gelmektedir.

Pointer örneği ile farklılıkları ve benzerliklerini görmeye çalışalım :


int main() 
{ 
    int& val;
} 
*****************************
AÇIKLAMA : ilk değer vermek zorunluudur.
*****************************
CEVAP    : syntax error
*****************************
int main()
{
    int val = 5;
    int* ptr = &val;

    *ptr;  
}
*****************************
AÇIKLAMA: ptr değişkenini kullanmak val değişkenini kullanmak anlamına gelir.
*****************************
int main()
{
    int val = 5;
    int* const ptr = &val;      
}
*****************************
AÇIKLAMA: kendisi const olduğu için ptr'ye başka değer atanamaz.(high level const)
*****************************
int main() 
{
    int val = 5;
    int& rval = val;

    rval = 6;       
}
*****************************
AÇIKLAMA: nesnenin yerine geçen isim olarak ifade etmiştik. Yani val değişkenine 6 değeri atanmıştır.
*****************************
int main()
{
    int val = 5;
    int val1 = 6;

    int& rval = val;

    rval = val1;               
} 
*****************************
AÇIKLAMA: rval val1 değişkenine reference oldu demek değildir. 
(val = val1 ile aynı anlamdadır)
*****************************
int main()
{
    int val = 5;

    int& ref1 = val;
    int& ref2 = r1;
    int& ref3 = r2;
    int& ref4 = r3;

    ++ref1;
    ++ref2;
    ++ref3;  
    ++ref4; 
    
    cout << ref1 << " " << ref2 << " " << ref3 << " " << ref4; 
} 
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 6 6 6 6
*****************************
int main()
{
    int val = 5;

    int* ptr = &val;

    int& ref = *ptr;

    ++ref;
    
    cout << ref << " " << val; 
} 
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 6 6
*****************************
int main()
{
    int arr[] = { 1, 2, 3, 4, 5 };

    int* ptr = arr;        //array to pointer conversion(int* ptr = &arr[0];)

    int* &ref = ptr;
    
    ++ref;                 // ptr = arr + 1
    ++*ref;

    for (auto lst : arr)  
    {        
        cout << lst << " ";
    }
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 1 3 3 4 5
***************************** 
int main ()
{
    int& ref = 5;
}
*****************************
AÇIKLAMA : r value reference değişkene l value reference değeri ile ilk değer verilemez.
*****************************
CEVAP    : syntax error
***************************** 

Değer kategorileri(value category) C++(11) ile beraber 3 ana başlıkta incelenmektedir.

  1. L value reference (left value)

  2. X value reference (expiring value)

  3. PR value reference (pure R value)


ree








  • Yukarıdaki şekilde kategorileri inceleyecek olursak; gL value(generalized L value) bir kimliği (identity) olan değer yani ilgili ifade ile nesneye erişim sağlanabilmesi durumudur. R value taşınabilir değer kategorisini, X value ise hem taşınabilir hem de bir kimliği olan kategoriyi ifade etmektedir.

int main() 
{ 
    int arr[3]{0];
}
*****************************
AÇIKLAMA : a[0] değer kategorisi L value reference
*****************************
int main()
{
    int val = 5;
    ++val;
    val++;
}
*****************************
AÇIKLAMA : ++val L value category, val++ ise PR value olarak değerlendirilir.
*****************************
int main()
{
    int val = 29;
    int val1 = 5;

    (val > val1 ? val : val1) = 94;
}
*****************************
AÇIKLAMA : Virgül operatörünün sağ operandı hangi değer kategorisinden ise üretilen ifade de o değer kategorisindendir.
*****************************
void smart_code(){}

int main()
{
    smart_code(); 
}
*****************************
AÇIKLAMA : fonksiyon ismiyle yapılan çağrılar PR value değer kategorisindendir.
*****************************
#include <iostream>
#include <algorithm>
#include <iterator>

using namespace std;

string& smart_code(string& str) 
{
    reverse(str.begin(), str.end());
    
    return str;
}

int main() 
{
    string s_code = "cpp";

    cout << s_code << endl;
    
    cout << smart_code(s_code) << endl;

    return 0;
}
*****************************
AÇIKLAMA : fonksiyon geri dönüş değeri reference ise L value değer kategorisindendir.
*****************************
int main()
{
    const int val = 5;
    int& ref = val;
}
*****************************
AÇIKLAMA : const int& türünden int& türüne dönüşüm geçersizdir.
*****************************
CEVAP    : syntax error
***************************** 
int main()
{
    int& ref = 5;
}
*****************************
AÇIKLAMA : 5 PR value olduğu için atama geçersizdir.
*****************************
CEVAP    : syntax error
***************************** 
int smart_code()
{
    return 1;
}

int main() 
{ 
    int& ref = smart_code(); 
} 
*****************************
AÇIKLAMA : üstte anlatılan değer kategorilerinden nedenini çıkarım yapınız.
*****************************
CEVAP    : syntax error
***************************** 
int smart_code()
{
    return 1;
}

int main()
{
    const int& ref = smart_code(); 
}
*****************************
AÇIKLAMA : reference burada bir sabit yerine geçer ona reference olur. Compiler geçici nesne oluşturup referansı da o geçici nesneye bağlar.
"int temp = foo();
const int &r = temp;"
*****************************
CEVAP    : legal
***************************** 
int smart_code(int& val)
{
    return 1;
}
*****************************
AÇIKLAMA : Fonksiyona geçirilen parametre L value kategorisinde olmalıdır.
*****************************
int smart_code(const int& val)
{
    return 1;
}
*****************************
AÇIKLAMA : Fonksiyona geçirilen parametre L value ya da R value kategorisinde olabilir.
*****************************

Pointer ve Reference Semantiklerinin Karşılaştırılması

  • İlk değer vermek referanslarda zorunludur ancak pointer böyle bir zorunluluk oluşturmaz.

int main()
{
    int* ptr;      // legal
    int& ref;      // syntax error
}
  • Bir pointer değişken başka bir pointer değişkenin adresini tutabilir.(pointer to pointer)

int main()
{
    int val = 5;
    int* ptr = &val;
    int** pptr = &ptr;
}
int main()
{
    int val = 5;
    int& ref1 = val;

    int& ref2 = ref1;    
}
*****************************
AÇIKLAMA : ref2 de burada val değişkenine reference olur.
*****************************

  • Elemanları dizi olan bir pointer array olabilir ancak referans yapıları bunu desteklememektedir(modern c++ ile eklenen wrapper incelenmelidir...)

int main()
{
    int* ptr[5];    
}
*****************************
AÇIKLAMA : elemanları int* olan bir dizidir(pointer array)
*****************************
int main()
{
    int v1, v2, v3, v4, v5;

    int& arr[] = { v1, v2, v3, v4, v5 };
}
*****************************
AÇIKLAMA : referans dizisi olarak bildirilen arr dizisi geçersizdir.
*****************************
  • null pointer kavramı vardır ancak referanslarda yoktur. Kullanım yerlerini inceleyecek olursak :

Fonksiyon geri dönüş değerine göre kullanımı :

int* smart_code()
{
}
*****************************
AÇIKLAMA : Fonksiyon başarılı olursa int türden bir nesnenin adresini döndürür, başarısız olursa nullptr döndürür.
*****************************

Arama fonksiyonlarında kullanımı:

#include <iostream>
#include <string>

using namespace std;

class Student {
public:
    string m_name;
    int    m_num;

public:
    Student(const string& name, int num) : m_name(name), m_num(num) {}
};

Student* search_student(const string& name, Student* students[], int cnt) 
{
    for (int i = 0; i < cnt; ++i) 
    {
        if (students[i] != nullptr && students[i]->m_name == name) 
        {
            return students[i];
        }
    }
    
    return nullptr;
}

int main() 
{
    Student* s1 = new Student("Betul", 04122021);
    Student* s2 = nullptr;
    Student* s3 = new Student("Yusuf", 20112022);

    Student* students[] = { s1, s2, s3 };
    int cnt = sizeof(students) / sizeof(students[0]);

    std::string search_name;
    std::cout << "Enter the name of the student to search for : ";
    std::cin >> search_name;

    Student* found = search_student(search_name, students, cnt);
    
    if (found != nullptr) 
    {
        std::cout << "Found!" << std::endl;
        
        cout << "name:" << found->m_name << ", num:" << found->m_num << endl;
    } 
    else 
    {
        cout << "Not found." << endl;
    }

    delete s1;
    delete s3;

    return 0;
}
*****************************
AÇIKLAMA : Aranan bulunamazsa nullptr döndürecektir ve not found yazdırılacaktır.
*****************************
  • parametresi pointer olan fonksiyonlarda kullanımı :

time_t time (time_t *p);
*****************************
AÇIKLAMA : çağıran kod ya nesne adresi ya da nullptr isteğinde bulunur. time_t türünden bir nesnenin adresini alır ona kendi timer değerini yazar. Hiçbir timer set edilmesi istenmiyorsa nullptr gönderilmelidir.
*****************************
fflush(FILE *f)
*****************************
AÇIKLAMA : hangi dosyanın buffer'ı flush edilecekse onun handle'ını alır. nullptr gönderilirse tüm dosyaların buffer'ını flush eder.
*****************************

NOT : c++17 ile eklenen std::optional bunu sağlamanın yollarından biri olarak dile eklenmiştir.


struct SmartCode {       
    int val, val1;
};

SmartCode func1();
SmartCode& func2();
SmartCode&& func3();

int main()
{
    int mx = 5;              
    const int cx = 6;
    int& rval = mx;

    int&& mx = mx + 5;
    SmartCode sc;
    SmartCode* ptr = &sc;
    SmartCode& ref{ mx };
    int arr[10];
}
*****************************
AÇIKLAMA : 
//mx          // L value expression(bir değişken ismi olan ifade)
//mx + 4      // PR value expression (aritmetik operatorle oluşturulan ifade)
//&mx         // PR value expression(adres op. ile oluşturulan ifade)
//&cx         // PR value expression(nesnenin const olması etkilemez)
//cx          // L value expression(atama yapılamaz)
//++mx        // L value expression
//mx++        // PR value expression
//(mx, cx)    // sağ operand(yani y)L value ise L value olur(C ve c++ farkı)
//mx>3?mx:cx  // L value (her ikisi de L value)
//mx>1?mx:2   // PR value expression
//mx>cx       // PR value expr (karşılaştırma op.
//mx&&cx      // PR value expression(logic operatorler)
//+mx         // PR value expression(işaret op)
//-mx         // PR value expression
//(double)mx  // PR value expression(tür dönüştürme ile oluşturulan)
//func1()     // PR value expression(geri dönüş değeri ref değilse PR)
//func2()     // L value expression
//func3()     // X value expression
//rval        // L value expression
//ref         // L value expression(ref bir isim formunda)
//sc          // L value expression
//sc.mx       // L value expression(. op. ile oluşturulan bir isim çünkü)
//ptr         // L value (bir nesnenin ismi)
//*ptr        // L value (içerik ile oluşturulan)
//&ptr        // PR value (adres op. ile oluşturuldu)
//ptr->arr    // L value expr. (isim formunda)
//&ptr->arr   // PR value expression
//ptr + 1     // PR value (+ op. ile)
//arr         // L value expression  
//*arr        // L value expr.
//arr + 3     // PR value expression
//arr[4]      // L value expression
//ref         // L value expression
//ref.arr     // L value expression
//int()       // PR value expr. (temporary object)
//int{}       // PR value expr. (temporary object)
//SmartCode() // PR value expr. (temporary object)
//SmartCode{} // PR value expr. (temporary object)
*****************************

int& smart();     
int code();      

int main()
{
    int x = 10;
    int y = 30;

    //uniform init(modern c++)
    const int primes[]{2, 3, 5, 7, 11 };   

    // legal
    int arr[]{ 29, 5, 94 };                    
    // referansa ilk değer vermemek syntax error
    int& r1;                               

    // r2 L value, ++x de L value legal
    int& r2(++x);                          

    // r3 L value, 10 R value expr. syntax error
    int& r3{ 5 };                         

    // int() geçici nesne PR value ifadedir ve 
    // const referanslar PR value ifadeye bağlanabilir.
    const int& r4{ int() };                
             
    // int() geçici nesne PR value ifadedir ve 
    // const referanslar PR value ifadeye bağlanabilir.
    const int& r5{ int{} };                
    
    // syntax error. +y PR value expr. ancak r6 L value
    int& r6 = +y;                          

    // legal, y L value old. için ifade de L value
    int& r7 = (x, y);                      

    // legal, her ikisi de L value ifade L value
    int& r8 = x > 10 ? x : y;              

    //  legal, smart() geri dönüş değeri & old. için L value
    int& r9 = smart();                        

    // error, code() R value expr.
    int& r10 = code();                       

    // syntax error, primes const ancak reference(r11) const değil.
    int& r11 = primes[2];                  

    // yukarıdaki ile aynı legal oldu.
    int const& r12 = *primes;              

    // legal. const olmayan x const a bağlanabilir.
    const int& r13{ x };                   

    /*arr bir dizi ancak reference tanımlandığında dizinin öğesinin adresine          
dönüştürülmez. */
    /*r14 referansını "int*" türüne bağlayamayız. syntax error */
    int*& r14 = arr;   
                      
    // syntax error. [] içi boş old. için.
    int(&r15)[] = arr;         

    // legal
    int(&r16)[2] = arr;        
}
int& smart(int& r1, int& r2)
{
    r1 += r2;

    return r1;
}

int code(int& r1, int& r2) 
{ 
    r1 *= r2; 

    ++smart(r1, r2);
    ++smart(r2, r1); 
}

int main()
{
    int val = 2, val1 = 5;

    code(val, val1);

    cout << val << " " << val1 << endl;
}
*****************************
AÇIKLAMA : fonksiyon parametreleri reference olduğu için 
r1 = val, r2 = val1 denebilir.
*****************************
CEVAP    : 16 22
***************************** 
int main()
{
    int val = 2;
    const int& rval = val > 0 ? val : 2;   
    // val > 0 = val : 2 ifadesi R value expr.
                  
    val = 5;
    cout << val << " " << rval;
    
    return 0;
}
*****************************
AÇIKLAMA : val > 0 ve 2 olması gerek ancak, bizim referansımız val'e değil derleyicinin oluşturduğu geçici nesneye bağlanır. 
Çünkü const referansa sağ taraf değeri ile ilk değer verince derleyici geçici nesne oluşturur ve bu const reference bu geçici nesneye bağlanır. rval vale reference değil bu yüzden val 5 olur.
*****************************
CEVAP    : 5 2
***************************** 
int main() 
{ 
    int val = 1;
    int x = 6; 
    
    // val > 0 = val : 1 ifadesi L value expr. 
    const int& rval = val > 0 ? val : x;        
                             
    val = 5; 
    cout << val << " " << rval; 
    
    return 0;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 5 5
***************************** 
int smart_code(int &val, int& val1)
{
    val  = 5;
    val1 = 5;

    return val * val1;
}

int main()
{
    int x = 1;
    int y = 2;
    int z = smart_code(x, x);
    
    cout << x << y << z << endl;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 5225
***************************** 
int main()
{
    int val{ 1 }, val1{};
    
    const int &ref1 = val > 0 ? val : val1++;
    const int &ref2 = val > 0 ? val : ++val1;
    val = 5;
    cout << ref1 << ref2;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 15
***************************** 
int& smart_code(int val)
{
    return val;
}

int main()
{
    int val1 = 2;
    smart_code(val1) = 4;
}
*****************************
AÇIKLAMA : 
otomatik ömürlü bir nesnenin adresiyle geri dönmek tanımsız davranıştır. 
otomatik ömürlü bir nesneye referans ile dönmekte tanımsız davranıştır.
*****************************
CEVAP    : undefined behaviour
***************************** 
int main()
{
    const int x = 20;
    auto y = x;           // constluk düşer.
                          // syntax hatası yok çünkü y const değil.
    ++y;                  // y burada x değeri ile hayata geldi.

    cout << y << " " << x << endl;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 21 20
***************************** 
int main()
{
    int val = 10;
    
    int& r1 = val;   // r1 val e referans (r1 demek val demek)

    auto r2 = val;   // r2 reference değil
    
    auto& r3 = r2;   // r2 val e referans değil haliyle (r3 r2 ye referans)     

    r2 += 5;         // r2 = 15
    r3 += 20;        // r3 = 35
    ++val;           // 11

    cout << r1 + r2 + r3 << endl;
}
*****************************
AÇIKLAMA : 11 + 35 + 35
*****************************
CEVAP    : 81
***************************** 
int main()
{
    int val = 10;
    
    const int &cr = val;    // legal
    auto& rval = cr;        

    ++rval;                 
}
*****************************
AÇIKLAMA : constluk düşmez. çıkarımı yapılan tür const int&
*****************************
CEVAP    : syntax error
***************************** 
int main()
{
    int arr[] = { 0, 1, 2, 3, 4, 5 };
    
    auto r1 = arr;        // int* r1 = arr demektir.
    auto &r2 = arr;       // r2 arr dizisine referans. array decay olmayacak.

    ++r2[3];              // 3 + 1

    cout << (r1[3] == r2[3]) << endl;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 1
***************************** 
int main() 
{ 
    int arr[] = { 10, 20, 30, 40 }; 
    
    auto pa = arr;       // int *pa = arr demek. (ilk öğenin adresini tutar)
    auto& rval = pa;     // rval demek pa demek.
    ++rval;              // bir sonraki öğeyi artırdık. 20 yi gösterdi.
    ++pa;                // bir sonraki öğenin adresi. 30 u gösterdi.
 
    cout << *rval << endl; 
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 30
***************************** 
// aldığı nesneyi döndüren bir fonksiyon demektir.
int &smart_code(int& rval)         
{
    ++rval;
    return rval;
}

int main()
{
    int val = 10;

    auto fval = smart_code;       // int&(*fval)(int &) = smart_code; 
    
// burada () operandı koyulmadı. auto& şeklinde olmadığı için fval bir 
   function pointer ve val 11 oldu.

    auto val1 = fval(val);       // fval in gösterdiği fonksiyon çağırılır.
                         
// val1 referans olmadığı için val ile alakası yok. 
// val1 11 oldu ama val değil.


    // val tekrar artırıldı ve 12 oldu
    auto &rval = fval(val); // rval de val değişkenine referans oldu.

    rval += 400;    // rval = 412
    val1 += 50;     // 61

    cout << "val : " << val << endl;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : 412
***************************** 
int main()
{
    int val = 10;
    int* ptr = &val;

    auto r1 = val;      // referans değil. (int r1 = val;)
    auto r2 = *ptr;     // *ptr demek val demek.
    auto r3 = r2;       // r3 de referans değil.
    auto& r4 = ptr;     // r4 bir pointer a referans (r4 ptr ye referans)
    auto& r5 = *ptr;    // r5 ise val değişkenine referans. (*ptr = val)


    r1 += 3;            // 13
    r2 += 13;           // 23
    r3 += 20;           // 30
    *r4 += r2;          // 
    ++r5;               //

    cout << "val : " << val << endl;
}
*****************************
AÇIKLAMA : 
*****************************
CEVAP    : val : 34
***************************** 
// ilk değer verilmek zorundadır.
auto a;      

// syntax error. referansa ilk değer vermemek error.
int& b;      

// legal
auto c = 10; 
  
// legal
int& d = c;  

// legal. const referansa PR value ile ilk değer verilir.
const auto &e = 20;  

// ++c L value int& L value old. için legal
int& f = ++c;        

// c+ 5 PR value. syntax error.
int &g = c + 5;      

// c%2 R value expr. int&& R value legal.
int&& h = c % 2;     

// geri dönüş değeri int olduğu için R value. int&& R value. legal.
int func(); 
int&& j = func();    

// int & geri dönüş değeri L value.
int& foo();          

// int&& R value. syntax error.
int&& m = foo();     

// R value
int ival = 10;      
      
// R value to R value legal
int& &rval = ival + 10;   

// rval R value ancak ifade içinde kullanıldığında bir isim value category olarak L value int& da L value o yüzden legal.
int& p = rval;            
                          

 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comments


bottom of page