Let's Connect

Software engineer and tech tinkerer. I love building cool things and learning new stuff.

15 Posts
5 Categories
7 Tags
Topics
Blog
C++ Classes & Data Abstraction
+
  • C++ dilinin en güçlü olduğu alanlardan bir tanesi data abstraction.

  • Nesne yönelimli programlama data abstraction demek değil. Data abstraction anlamı veri soyutlama.

  • Problem domaininde ki bir veriyi yazılımsal olarak nasıl temsil edeceğim demek. Hikaye şöyle, user defined türlerimiz olmasa elimizde sadece primitive türler var. Bu türlerle modellenebilecek veriler var. Ama problem domaininde bizi ilgilendirecek verilerin çoğu daha komplex. a + bi gibi bir sayı gibi yada bir üniversitede okuyan bir öğrencinin bilgileri.(tüm bilgileri) En güzel örnek olarak programlama kitaplarında genelde tarih bilgisi veriliyor. Tarih bilgisi tipik olarak gün ay yıl. Dilin bize sunduğu avantajlar olmasaydı şöyle yapıcaktık, 3 tane değişken alacaktık, dilin kuralları gereği bu 3 değişken arasında bir bağlantı kurulmasa da dilin araçlarıyla logic olarak biz bir bağlantı kuracaktık. Bunun da getirdiği bir sürü problem olacaktı. Data abstraction yönünden güçlü olması demek daha komplex türleri primitive türleri kullanıyormuş gibi kolaylıkla kullanabilmektir. İşte bu konuda C++ dilinde class(sınıf)lar var.

  • Şimdi bahsedeceğim C++ dilinin sınıf kavramı. Nesne yönelimli programlamanın sınıfı değil.

  • Sınıf konusu C++ dilinde user defined bir tür.

  • class bir keyword.

  • Bu türü doğrudan Data ismi ile kullanılabilir

class Data{}; // semicolon unutma - noktalı virgül

\\-----

Data x; // bir nesne tanimladik
Data *ptr = new Data; // bir dinamik nesne tanimladik

Sınıf Öğeleri

  • Blok içinde sınıfın öğelerini tanıtacağız

  • Veri öğeleri (Data member)

  • Fonksiyonlar öğeleri (Member function)

  • Tür öğeleri (Nested type)

  • C de birlikler ve yapılar artık c++ da sınıf oldu.

  • Sınıf üye fonksiyonları semantik bir maske, gizleme.

  • class değilde struct ile bir sınıf oluşturulsaydı default erişim bölümü public olucaktı, public class yerine struct kullanılır

  • class bir scope fakat public, private, protected birer scope değil.

  • C++ dilinde sınıf dışı gördüğümüz fonksiyonlara global fonksiyon diyeceğiz. Sınıf içi tanımlanan fonksiyonlara member function.

  • Structlardan farklı olarak, sınıfların erişim ayrıcalığı var.

  • Sınıfın public, protected ve private bölümleri var.

// ERISIM BELIRLEYICILERI
class Data
{
public:
    /////
private:
    /////
protected:
    /////
}
  • private sınıfın kendi kodlarına açık fakat sınıfın müşterilerine kapalı. compile timeda kontrol edilecek.

  • protected, kalıtımla birlikte önem kazanacak(inheritance). Sınıfın clientlarına kapalı, inherit edilince açık.

  • .(nokta) operatörü

  • ->(ok) operatörü

  • :: (çözünürlük operatörü)

  • Aynen yapılarda olduğu gibi bir sınıf nesnesi yoluyla sınıfın öğesine nokta yoluyla ulaşabiliyoruz. Önemli olan burada öğe aramanın nasıl yapıldığı.

  • Derleyici . operatörünün önce sol operandına bakar ve bir sınıf türünden olduğunu anlarsa sağ operandını class scope’ta arar.

  • Bir sınıf türünden pointer ise ptr->x ok operandının solu sınıf mı, sağ operandını o class içinde arar

myclass.*ptr;
ptr->x;

Name Lookup

  • Derleyici aradığı ismi bulunamassa her zaman error. Bulursa geçerli diye birşey yok. Aramada 3 durum var
  1. nokta operatörü
  2. -> operatörü (o sınıf türünden adres olucak)
  3. :: operatörü (sınıf türünden olucak)
  • operatörlerinin sağ operantları class scope ta aranır
Myclass m;
m.x; // isim arama önce yapılıyor, sonra class private olursa x aradım buldum, private alanına erişim hatası
  • private kodlarını gizlemek için PIMPL idiomu kullanılır, bu idiomun hem compile time hemde run time maliyeti var.

  • En katı korunan kısım private, hem clientlara hem de türetilmiş sınıflara kapalı

  • protected clientlara kapalı, türetilmiş sınıflara açık

  • public herkese açık

  • C de şöyle diyebilirdik, client bunları bil ama kullanma, ben senin bu öğelere doğrudan erişmeni istemiyorum gibi.


PIMPL IDIOMU HAKKINDA

  • Madem amaç private bölümü gizlemek, engellemek o zaman neden private sınıf tanımında yazıyoruz ki? Muhtemelen başlık dosyasında olucak, eee madem clientların bilmesini istemiyorum başlık dosyasına koyarsam clientlar bunu görmeyecek mi? Malesef görecekler, böyle bir açık var ama teknik zorunluluktan oluyor. Neden? Çünkü client ın bunu kullanması yanında cpp dosyasıda bunu kullanıyor. Özel önlem alınabilir ama default durum client bunu görebiliyor, bunları göstermemenin yolu var buna pimpl idiomu deniyor. (Pointer Implemantatıon)

  • Compile time maliyeti var, runtime maliyeti var (pimpl idiomu)

  • Veri elemanları private bölümü, member functionlar sınıfın public bölümüne, sınıfın public bölümü genelde bu sınıf için işleri yapıcak fonksiyonlardan oluşacak

  • Pimpl idiomu için ayrıcı bir makale yazıcam. Konuyu dağıtmamak için devam ediyoruz…. Bir önce ki makalede de belirttiğim gibi :: çözünürlük operatörü ile ilgili şöyle ufak bir örnek yapalım.

// :: operatoru
#include <iostream>


using namespace std;

int x = 56;

int main()
{
    int x = 20;

    cout << x << endl;      // 20
    cout << ::x << endl;      // 56

    return 0;
}
// Global alanda x isimli birsey bulunamadi

#include <iostream>


using namespace std;


int main()
{
    int x = 20;

    cout << x << endl;
    cout << ::x << endl;    // No member named 'x' in the global namespace; did you mean simply 'x'?

    return 0;
}
  • Üye fonksiyon uydurmasının anlamı şu, daha önce global scope ta bulunan fonksiyonlar artık class scope içine dahil oluyorlar, amaç clienta daha iyi bir interface vermek, mydate.set() mydate in set fonksiyonu gibi.

Artık üye fonksiyonlar geldi, global fonksiyonlar hiç kullanılmayacak mı?

  • Hayır alakası yok, C de zaten başka yolumuz yok ama C++ da sınıfla ilgili iş yapan fonksiyonların artık çoğu class içinde olucak ama global fonksiyonlarda olabilir
//Function Member

#include <iostream>

using namespace std;

int x = 9999;

class Data {
    int x;
public:
    void set(int val);
    void display();
};

void Data::set(int i)
{
    x = i;
}

void Data::display()
{
    cout << "x = " << x << endl;
}

int main()
{
    Data mydata;

    mydata.set(100);
    mydata.display();

    return 0;
}

Uygulama pratiğinde her sınıf için bir header dosyası oluşturulur

// Multiple Inclusion || Include Guard
#ifndef MYHEADER_H
#define MYHEADER_H
// header file contents go here..
#endif // MYHEADER_H
// Scope

#include <iostream>

using namespace std;

int x = 9999;

class Data {
    int x;
public:
    void set(int val);
    void display();
};

void Data::set(int i)
{
    x = i;
}

void Data::display()
{
    int x = 3333;
    cout << "x = " << x << endl;         // x = 3333
    cout << "x = " << Data::x << endl;     // x = 100
    cout << "x = " << ::x << endl;         // x = 9999
}

int main()
{
    Data mydata;

    mydata.set(100);
    mydata.display();

    return 0;
}
#include <iostream>

using namespace std;


class Date {
    char str[1000];
public:
    void set(int d, int m, int y);
    void display();
};



void Date::set(int d, int m, int y)
{
    sprintf(str, "%d/%d/%d", d, m, y);
}

void Date::display()
{
    cout << str << endl;   // 5/12/1997
}



int main()
{
    Date mydate;

    mydate.set(5, 12, 1997);
    mydate.display();

    return 0;
}
#include <iostream>

using namespace std;

class Myclass {
    int x, y;
public:
   void func();
};



//////myclass.cpp


void Myclass::func()
{
    cout << "this = " << this << endl;   // this = 0x7fff5fbff760
}

int main()
{
    Myclass m;

    m.func();

    cout << "&m   = " << &m << endl;   // // &m = 0x7fff5fbff760

    return 0;
}

Adres Bilgisi

  • Sınıfta public veya private önce olacağı durumlar olucak, belli senaryolar durumda bunda özgürüz.

  • Global bir fonksiyon ile sınıfın üye fonksiyonu asla overload değildir.

  • Sınıfın üye fonksiyonu default u nonstatic

  • Eğer sınıf içinde bir fonksiyonda static keywordu varsa bunu C deki ile karıştırmayacağız. Bu sınıf nesnesinin adresine ihtiyaç duymayan demek


Sınıfın Üye Fonksiyonları Arasında İsim Arama (name lookup)

  • non static, nesneye ihtiyacı var(static keywordu almayanlar)

  • C dilinde block scope, global scope alanı vardı, şimdi class scope eklendi.(C++ için)

  • Block scope un class scopa önceliği var.

  • isim arama yapılır, eğer aranan isim bulunursa arama durur (javada böyle değil), yani isim 1 kez aranır, bulunan isimden sonra asla isim arama yapılmaz.


//Scope && Erişim Konusu

#include <iostream>

using namespace std;


class Data {
    int x;
public:
    void func();
};

Data g;


void Data::func()
{
    Data y;
    // sinifin uye fonksiyonu, private bolume erisebilir
    x = 12;     // class scope, bu fonksiyon hangi x icin cagirirsam onun ismi
    g.x = 13;    // global nesne, private erisimi var
    y.x = 14;    // yerel nesne, private erisimi var
}

Const Öğeler

  • Const öğelerin amacı nesneyi değiştirmek değil, bu bir get fonksiyonu, yani bu nesnenin varlık nedeni nesneyi değiştirmek değil

  • Const olmayan bir üye fonksiyon const üye fonksiyonu çağırabilir ama const üye fonksiyon const olmayan bir üye fonk çağıramaz.

  • Const overloading (yine function overloading tabi ama const)

  • Native erişim biçimi, this göstericisi ile ve çözünürlük operatörü ile

  • C++ dilinde this->x olarak kullanmayın… (java, c# taki gibi değil), normal koşullarda bunu kullanma

  • Sınıfın veri elemanlarına this gösterici ile erişilebiliriz


This Pointerı

  • Bu fonksiyon hangi nesne için çağırıldı ise o nesnenin adresi

  • *this demek o adresteki nesne

  • this kendisi const olan bir gösterici

// this

#include <iostream>

using namespace std;

class Myclass {
    int x, y;
public:
   void func(Myclass &r);

};

Myclass g;

void Myclass::func(Myclass &r)
{
    *this = r;
    ////
}

int main()
{
    Myclass m1, m2;
    ////

    m1.func(m2);        // m2'yi m1'e atiyoruz
    // *this m1 demek zaten, *this nesnenin kendisi demek
    return 0;
}
  • Nesnenin kendisi ise nokta operatörü

  • Pointer ise ok(->) operatörü kullan

// Genel Bir Ornek

#include <iostream>

using namespace std;

///date.h
class Date {
    int md, mm, my;
public:
    Date &set(int d, int m, int y);
    void display()const;
    int getMonthDay()const;
    int getMonth()const;
    int getYear()const;
};

//////////date.cpp
//include "date.h"

Date &Date::set(int d, int m, int y)
{
    md = d;
    mm = m;
    my = y;
    return *this;
}


int Date::getMonthDay()const
{
    return md;
}

int Date::getMonth()const
{
    return mm;
}

int Date::getYear()const
{
    return my;
}

void Date::display()const
{
    cout << md << "/" << mm << "/" << my << endl;
}

int main()
{
    Date x;

    x.set(12, 4, 1999).display();      //    12/4/1999

    int val = x.getMonth();

    Date y;

    y.set(23, 5, 1987);


    return 0;
}

Ufak bir tekrar

  • Sınıflarda .(nokta) operatörü

  • Pointerlarda ->(ok) operatörü

  • :: çözünürlük operatörü(erisim) Myclass::x

  • Sadece ::x dersek global değişken

  • :: 2 kullanımı var… SINIFLARDA ve NAMESPACE alanlarında

  • cons olan fonksiyon, cons olmayan üye fonksiyonu çağıramaz

  • cons olmayan bir fonksiyon, cons olan fonk çağırabilir

Üyeleri 3 ayrı erişim var

  • Native doğrudan erişim x = 45; y = 26; gibi

  • this ile erişim

  • çözünürlük operatörü(::) ile erişim

Dereference edersek;

  • this, Myclass m; m.func();
  • *this m’nin kendisi yani hangi nesne için çağırılırsa o nesnenin kendisi
  • this kendisi const olan bir göstericidir. Data *const this;
  • this atama yapamayız ama *this e atama yapabiliriz
this -> x++; ile (*this).x++; aynıdır
C & C++ Dinamik Bellek Yönetimi & Extern C Bildirimi
+

Bu konuda fazla derine inmeden c dilinden c++ diline geçerken dinamik bellek yönetiminde bizi bekleyen bazı değişikliklerden bahsetmek istiyorum. Aynı zaman da extern c bildirimi ve interface tasarımı ile ilgili birkaç noktaya değinmek istiyorum.

  • C dilinde stdlib kütüphanesinde ki fonksiyonları kullanıyorduk.. (malloc, alloc vs.)
  • C++ dilinde bunlara gerek kalmayacak. Yeni operatörlerimiz var ismi new olan operatörler ve ismi delete olan operatörler.
  • new operatörler nesneyi HAYATA GETİRİR.
  • delete dinamik nesnelerin HAYATINI SONLANDIRIR.
  • malloc hayat başlatmıyor sadece yer ayırıyordu ama new nesneyi hayata getiriyor.(initialize etme, yazma) Bellekte bir nesnenin yerinin olması onu aslında nesne yapmıyor. Storage nesnenin gerek şartı ama yeter şartı değil.

NEDEN ?

  • Primitive türler olduğunda şu değişkene bir veri atarım diyebiliriz fakat sınıf olduğunda olay bu kadar basit olmayacak. O bellek alanın kullanılabilir bir hale getirilmesi için birçok şey yapılacak(kaynaklar edinilecek vs vs)
  • Yer elde etmek başka birşey, elde edilen yerde bir nesneyi hayata getirmek başka birşey.
  • Bellekte yeri olmayan nesneden bahsetmek mümkün değil.
  • new operatörünün 4–5 biçimi var, biz şimdilik 2 biçiminde bahsedeceğiz.
  • Primitive türlerden örnek: new int → int türden bir dinamik nesneyi hayata başlatıyorum
#include <iostream>
#include <stdlib.h>


int main() {

    int *p1 = new int;
    int *p2 = new int[10];

    // kodlar

    delete p1;
    delete []p2;

    return 0;

}
#include <iostream>
#include <stdlib.h>

int main() {

    int *p1 = new int{};        // *p1 = 0
    int *ptr1 = new int{12};      // *ptr1 = 12
    int *ptr2 = new int(20);      // *ptr2 = 20

    int *p2 = new int[10];

    /// ....

    delete p1;
    delete []p2;

    return 0;

}
  • auto keyword’ü tanıttığı ismi, ilk değer vereceğimiz ifadenin türü yapar.
  • memory leak ile resource leak birbirine karıştırmamak gerek.
  • memory leak → bellek sızıntısı, resource → kaynak sızıntısı
  • İşimiz bittikten sonra nesneleri delete etmemiz gerekiyor. Etmezsek bellek sızıntısı olur, birçok user defined tür için de kaynak sızıntı işi olur.

Basitçe arka planda ne oluyor ?

  • new operatörü 2 işi birden yapıyor (derleyicinin oluşturduğu kodda)
  • Hayata gelen nesne için bellek alan gerekiyor. sizeof_t kadar. Arka planda bunu operator new isimli fonksiyona yaptırıyor
  • new operatörü ile operator new fonksiyonu (karıştırılmamalı)
  • operator fonksiyonları, isimlerinin doğal parçası operator… Örnek toplama operatörü ise operator+ gibi.
  • operator new, malloc un parametrik yapısının aynısı.

C++ niye malloc kullanılmamış?

  • Exception handling için
  • new operatörü bellek elde etmek dışında, sınıf nesnelerinin hayata gelmesi için gerekli olan constructor fonksiyonunu çağrıyor.
  • delete ise destructor’ı çağırır, operator delete kullanır.
  • C++ dilinde default olarak garbage collector olmaması sonucunda delete operatörü var. Biz de hazır bir garbage collector yok.
  • Dinamik nesne oluşturma ile, dinamik nesnelerden oluşan bir liste oluşturma sentaksı farklıdır.
operator delete(ptr); // free'nin karsiligi

operator new   -> malloc
operator delete -> free

operator new // Diger gorevi CONSTRUCTOR cagirmak
operator delete // Diger goreci DESTRUCTOR cagirmak

// dinamik bir nesne hayata getirme
T *p = new T;

// exp kadar dinamik nesne olasaca
new int[exp];


// Cogul
T *p = new T[n];
delete []p;

int *p1 = new int; // COP DEGER
int *p2 = new int[10]; // COP DEGER


// Tekil ise
delete p1;

// Cogul ise
delete [p2]; // koseli parantez icine birsey yazilmaz

int *p1 = new int{};    // *p1 = 0
int *p1 = new int{12};  // *p1 = 12 ilk deger verilebilir

new int(10);    // 1 tane
new int[10];    // 10 tane
  • C++ 11 öncesi [] ile hayata getirilen primitive türlere ilk değer vermek yoktu. C++11 ile {} initializer list geldi.
int n = 5;
int *p = new int[n] {1, 2, 3, 4, 5};
  • Nesneleri kısmen öldürmek gibi birşey söz konusu değil. Toplu halde bildirim, toplu halde hayat bitirme.
// C Pointer, MALLOC
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    int row, col;
    int **pd; // Elemanlari int* turunden olan bir dizinin adresi int** turundendir
    int i, k;

    srand((unsigned)time(NULL));

    printf("Matrisiniz satir ve sutun sayisini giriniz: ");
    scanf("%d%d", &row, &col);

    pd = (int **) malloc(row * sizeof(int *));
    if(pd == NULL){
        printf("Cannot allocate memory!\n");
        exit(EXIT_FAILURE);
    }

    for(i = 0; i < row; i++){
        pd[i] = (int *) malloc(col * sizeof(int));
        if(pd[i] == NULL){
            printf("Cannot allocate memory!\n");
            exit(EXIT_FAILURE);
        }
    }

    for(i =0; i < row; i++)
        for(k = 0; k < col; k++)
            pd[i][k] = rand() % 10;


    for(i =0; i < row; i++)
        for(k = 0; k < col; k++)
            printf("%d", pd[i][k]);
        printf("\n");

    for(i = 0; i < row; i++)
        free(pd[i]);

    free(pd);

    return 0;
}
// C++
#include <cstdlib>
#include <ctime>
#include <iostream>

int main()
{
    int row, col;
    srand(static_cast<unsigned>(time(nullptr)));

    std::cout << "Matrisiniz satir ve sutun sayisini giriniz: ";
    std::cin >> row >> col;
    int **pd{new int *[row]};

    for(int i = 0; i < row; i++)
        pd[i] = new int[col];

    for(int i =0; i < row; i++)
        for(int k = 0; k < col; k++)
            pd[i][k] = rand() % 10;


    for(int i = 0; i < row; i++){
        for(int k = 0; k < col; k++)
            std::cout << pd[i][k];
        std::cout << "\n";
    }

    for(int i = 0; i < row; i++)
        delete []pd[i];

    delete[]pd;

    return 0;
}

Not: endl alt satıra geçirmekten baska bufferı flush eder. http://www.cplusplus.com/reference/ostream/endl/


Extern C Bildirimi

  • C++ programcısı C++ kaynak dosyasında C dilinde derlenmiş bir dosyayı çağırıyor. Mesela C dilinde yazdığımız bir isprime fonksiyonunu derledim ve sonra C++ projesi oluşturuyorum. Compile time’da sorun yok ama runtime da problem oluyor. Bu linkera bağlı bir problem

  • C dilinde function overloading yok. C dilinde function overloading olmadığı için C de derleyiciler dışsal referans olarak bir fonksiyona atıfta bulunduklarında(bir fonksiyon inline değil ise derleyici object koda fonk çağırıldığına yönelik bir isim yazıyor özel bir notasyonla linker için yazıyor).

  • C derleyicileri bir fonksiyon çağrısını dışsal referansa dönüştürdüklerinde sadece fonksiyon isminden haraketle bir fonksiyon oluşturuyorlar.

  • C++ derleyicileri linkera overloadlardan hangisini çağırdığını anlamaları için parametrelerin türleri ve sayısını belirten bir notasyonda kullanmak zorunda

  • Bu problem, derleyiciye anlatmamız gerekiyor, bu bildirime extern c bildirimi deniyor. Bunun amacı bu fonksiyon C dilinde derlenmiştir, bu fonksiyona yapılan çağrıyı C nin notasyonuyla yaz, derleyici bulsun demek.

// kerem.h - Extern C Bildirimi
#ifdef __cplusplus
extern "C" { //extern c birkez yazip, blok icine almak
#endif

    void k1(int);
    void k2(int);
    void k3(int);
    void k4(int);

#ifdef __cplusplus
}
#endif

C++ Interface Tasarımı Hakkında

  • Client’ın ihtiyaç duymuyacağı bir ismi header dosyasına koymayın. Tasarım her zaman dıştan içe doğru yapılır.(interface tasarımı)
  • Önce başlık dosyası, sonra cpp (önce interface, sonra implemantasyon)
  • Tasarım dışardan içeriye olacak, interface’ten → implemantasyon’a
C++ Function Overloading & Function Overloading Resolution
+
  • Konu içerisinde function overloading ve resolution mekanizmalarına değineceğiz. Derleyicinin arka planda bunu nasıl yaptığını, hangi aşamalardan geçtiğini anlatmaya çalışacağım.

  • Fonksiyonların aşırı yüklenmesi çevirisi yanlış bir anlama çıkabiliyor. Aşırı olan birşey iyi değildir. Burada fonksiyonların yüklenmesi diyelim.

  • C++ dilinde bu mekanizma var, C dilinde yok.

  • Function overloading basit olarak aynı isimli fonksiyonların bir arada bulunmasıdır.

  • Amaç: Client’ın işini kolaylaştırmak.


C dilinde bu mekanizmanın olmaması ne gibi sıkıntılar çıkarıyor ve neden yok?

  • C dilinde böyle bir yapının olmamasından dolayı şöyle sıkıntılar çıkmıştır. Her fonksiyon için ayrı bir isim oluyor. Örneğin mutlak değer fonksiyonu abs(), floating-point number için fabs(), long int için labs().. işte böyle bir sıkıntı söz konusu.

  • C dilinde olmamasının sebebi derleyicinin kodunu küçük tutmak. Çünkü böyle bir yapıda derleyecinin kodu çok daha fazla büyüyecek. Bu karmaşık derleyici yoran bir mekanizma. Derleyicisi en az %30 kadar büyütürdü.

  • Runtime maliyeti yok, çünkü ne kadar aynı isimli fonksiyon olsa da hangi fonksiyonun çağırıldığı, derleyici compile time da anlıyor. Yani linker a dışsal referans olarak bunu anladıktan sonra yazıyor.

  • Hangi fonksiyon çağrıldığı anlama, compile time da static binding yada early binding denir. Bind (ilişkilendirme, bağlama)


Dynamic binding | C++ OOP en önemli araçları

  • Late binding | Çalışma zamanı çokbiçimliliği (Runtime Polymorphism)

Derleyicinin hangi fonksiyonu çağırdığını nasıl anlar?

  • (Function Overload Resolution) derleyicinin dilin kurallarına dayanarak hangi fonksiyonu çağrıldığını anlama işine denir

  • (Fonksiyon imzası) bildirimden tür çıkartım, geriye kaç parametre, kaç değişken var ve türleri neler gibi.

  • İmza terimini duyduğunuzda, fonksiyon geri dönüş değerinin türü dahil değil.

  • Bildirim dediğimizde fonksiyon geri dönüş değeri türü dahil.


Fonksiyon Overloading dendiğinde 3 koşulun olması gerekiyor.

  1. Fonksiyon isimleri aynı olacak.
  2. Scopeları aynı olucak (aynı scope larda fonksiyonlar birbirini overload eder, farklı scopelardaki fonksiyonlar birbirini maskeler)
  3. İmzaları farklı olucak (parametre sayısı veya parametre turu)
  • Aynı scope ta aynı isimli 2 fonksiyon varsa ve function overloading yoksa 2 şey olabilir, ya error yada redeclaration(yeniden bildirim)

  • Function Overloading için distinct type olmalıdır.

  • C++11 de typedef bildirimlerinin yerine uydurulan ayrı bir araç var.

  • typedef isimleri distinct tür değildir, bu yüzden typedef isimleriyle function overload olmaz. C++11 de artık typedef bildirimlerinin yerine uydurulan ayrı bir araç var. “using Kerem int” gibi.

  • using bildirimi, typedef te olmayan ilave anlamlarda var.

typedef int Word;

int f1(int);
int f1(Word); // Yeniden Bildirim (Redeclaration)

// IMZALAR ayni, GERI DONUS DEGERLERIDE ayni
typedef int Word;

int f1(int);

double f1(Word); // GECERSIZ -> (Type Mismatch in Redeclaration)

void func(const int x);
void func(int x); // Yeniden Bildirim (Redeclaration)

Asla pointer olmayan bir parametreyi const yapmayın. Yaparsak ne olur?

  • Fonksiyon bildirimi var yada yok. Derleyici burada ki const anahtar sözcüğünü ciddiye almaz.

  • Neden ciddiye almaz? Çünkü bu call by value bir anlam yüklersek, x değerini değiştiremem der ve sen onu değiştirsende değiştirmesende benim değişkenime olan birşey yok. O yüzden beni ilgilendirmiyor der.

    1. pointer olmayan parametreleri bildirimde veya tanımda const yapmayın onun bir anlamı yok.
    2. derleyici bunu görmez
  • Eğer 2 parametre türleri aynı ise pointer değilse ve biri T türünden ise öteki const T türündense function overloading değil, errorde değil, redeclaration’dır. Bu cümle biraz karışık gelmiş olabilir. Örnek vererek acıklayalım.

void func(const int x);
void func(int x); // Yeniden Bildirim (Redeclaration)


// Geçerli, Const Overloading (çok kullanılan bir mekanizma)
void func(int &);
void func(const int &);


// Geçerli, Pointer Overloading
void func(int *);
void func(const int *);


// Geçerli, Biri CALL BY VALUE digeri VALL BY REFERENCE
void func(int);
void func(int &);


// Geçerli, Funtion Overloading (Default Parameter)
void func(int);
void func(int, int = 10);

Ufak tekrar (function overloading için olması gereken):

  1. Scope aynı olacak.
  2. İsimler aynı olacak.
  3. İmzalar farklı olacak.
  • enum -> int türüne dönüşüm var.

  • doğal türlerden -> enum türüne dönüşüm yok.

  • Farklı türden pointerlar arası dönüşüm yok

T *        ->     void * a dönüşür
void *     ->     T * a dönüşmez
  • Bir pointera tam sayı olarak 0 atarsanız tam sayı olan 0 null adresine dönüşür.

FUNCTION OVERLOAD RESOLUTION

  • Derleyicinin hangi fonksiyonu çağrıldığını anlama mekanizmasıdır.

  • İki şekilde sonuçlabilir. (ERROR yada LEGAL)

  • Yani ortada bir function overloading var diye yapılan her çağrı doğru olmayabilir.

  • Error ise 2 seçenek var,

  1. Yanlış function çağrılması(no match),
  2. Ambiguity (Çift anlamlılık hatası)
  • Aday fonksiyonlar,
  1. İlk aşamada sadece aynı isimde olmaları yeterli. Aynı scope içinde olucak. Farklı scope içinde ise maskeleme olur.
  2. İkinci aşamada derleyici uygun fonksiyonları bulmak ister. Bu aşamada hiç uygun fonksiyon bulamassa ERROR olur. Burada ki aşama uygun fonksiyonları bulup eleme yapma.
  3. Birden fazla uygun fonksiyon varsa, derleyici dilin kurallarına göre argümanlardan parametre değişkenlerine yapılan dönüşümü belirli kalite grubuna ayıracak. Bunlara bir renk vericek gibi düşünebiliriz. Yani her argümandan her parametre değişkenine dönüşümün kalitesi aynı değil. Eğer fonksiyon çağrısındaki argümanın diyelim ki birinci fonksiyona dönüşümü A kalitesinde ise ikinci fonksiyonun ilgili parametresine dönüşümü ondan daha düşük B kalitesinde ise A seçilecektir.

Yani bu aşama da derleyici fonksiyonları derecelendirecek. Bu derecelendirmeyi neye göre yapıyor?

  • Bir argümandan bir parametre değişkenine legal olarak atama yapılabiliyorsa dönüşümün kalitesi 4 kalite gurubundan biri olacaktır. (Yüksek kaliteden -> Düşük kaliteye)
1. EXACT MATCH (Tam Uyum)
2. PROMOTION (Terfi — Yükseltme)
3. STANDARD CONVERSION (Standard Dönüşüm)
4. USER DEFINED CONVERSION
  • Bir argüman bir parametre değişkenine değer atanırken, derleyici function overloading seçimi yaparken rank vericek, derecelendirecek
  • İkiside aynı ranka sahip ise bu ambiguity(en çok 3. ve 4. case için ambiguity oluyor)

EXACT MATCH

  1. Exact match tam uyumluluk olarak çevirebiliriz. Örneğin int argümanı int parametreye göndermek, float’u float’a göndermek exact matchtir.
  • Bazı özel durumlarda exact match kabul edilir,
  1. Eğer fonksiyon parametresi const T ise bu fonksiyonu T ile çağırırsak T dan const a dönüşüm exact match kabul edilir.
  2. func(int x); int y = 10; func(y); // sol taraf değerinden sağ taraf dönüşüm exact math l value ~> r value translation.
  3. dizi isimlerini sizeof() operatörü ptr gönderilmesi sonucunda dizinin ilk elemanı adrese dönüştürülüyor.
void func(int *ptr);

int a[100];
func(a);
func(&a[0]); // Array to Pointer Conversion
int func(int);  // &func -> fonksiyon adresi
int(*)(int);    // Adres func -> &func turu aslinda bu

// C dilinde bir hatirlatma
#include <iostream>

int func(int, int);

int main()
{
    int a[100];

    int *ptr = a; // ARRAY TO POINTER CONVERSION, 0 indisli elemanin adresine donusturuluyor

    int (*fp)(int, int) = func; // FUNCTION TO POINTER CONVERSION
            // &func demek yerine -> func demek yeterli
}

PROMOTION

  • int altı türlerin, int’e yükseltilmesi(c dilinde var)(integral promotion)

  • int altı türler(short, char, bool) int’e yükseltilmesi (Yeni gelen char türleri hariç.)

  • float türünden double dönüşümde promotion

  • Dikkat double türünden long double’a vs dönüşümleri promotion değil

  • int türünden unsigned int ve int türünden double dönüşümleri promotion değil


STANDARD CONVERSION

  • Argümandan parametre dönüşümü legal ama exact match ve promotion değil ise o zaman standard conversion’dır.

  • Örnek int türünden double atama legal

  • unsigned int türünden int türüne standard conversion

  • 0’dan pointer’a standard conversion

void func(double);

func(12);   // STANDARD CONVERSION

void func(unsigned int);
// int -> double       STANDARD CONVERSION
// int -> unsigned     STANDARD CONVERSION

//  İkiside Standard Conversion o zaman ambiguity

USER DEFINED CONVERSION

  • C dilinde olan birşey değil ama C++ dilinde var.

  • User define conversion, programcı tarafından oluşturulan tanımlanan dönüşümlerdir.

  • Bir dönüşüm, biz fonksiyon yazdık diye yapılıyorsa buna user defined conversion denir. O fonksiyon yazılmasaydı legal olmayacaktı.

struct Data{};
void func(struct Data);
func(10); // LEGAL DEGIL ERROR
// error: no viable conversion from 'int' to 'struct Data'

// Conversion Constructor
Data(int); // Bu fonksiyonun olmasi donusumu LEGAL KILIYOR
func(10); // LEGAL
#include <iostream>

void func(int *ptr)
{
    std::cout << "func(int *)" << std::endl;
}

void func(const int *ptr)
{
    std::cout << "func(const int *)" << std::endl;
}

int main() {

    int x = 10;
    const int y = 20;

    func(&x); // usteki void -> func(int *ptr)
    func(&y); // alttaki func cagirilacak void -> func(const int *ptr)

    return 0;
}
#include <iostream>

void func(int &r)
{
    std::cout << "func(int &)" << std::endl;
}

void func(const int &r)
{
    std::cout << "func(const int &)" << std::endl;
}

int main() {

    int x = 10;
    const int y = 20;

    // nesnelerin kendileri ile cagiracagiz
    func(x); // int & olan
    func(y); // cinst int & olan

    return 0;

}
#include <iostream>

/*
 * call by ref ve value'da FUNC OVERLOAD OLUSTURUYOR
 * call by val'nun vall by ref'a USTUNLUGU YOK
 */

void func(int &r)
{
    std::cout << "func(int &)" << std::endl;
}

void func(int)
{
    std::cout << "func(int )" << std::endl;
}

int main() {

    int x = 10;
    const int y = 20;

    func(x);  // CIFT ANLAMLILIK HATASI
    // error: call to 'func' is ambiguous

    return 0;

}
C++ Function Default Argument
+
  • C dilinde olmayan C++, Java, C#, Python gibi dillerde bulunan bir mekanizma.

  • C dilinde bir fonksiyonun kaç parametresi varsa o kadar argüman gönderilmesi gerekir. Variadic Function hariç tutuyoruz.

  • Böyle bir mekanizmanın olması client tarafından birçok fonksiyonun aynı değerlerle çağırılmasıdır. Parametre sayısı fazla olan fonksiyonlarda bu mekanizma daha fazla işe yarıyor. Programcının işini kolaylaştırıyor.

  • Default parametre mekanizması run time maliyeti getiren bir mekanizma değildir

#include <iostream>

void func(int x = 10, int y = 20, int z = 30);

void func(int x, int y, int z)
{
    std::cout << "x -> " << x << std::endl;
    std::cout << "y -> " << y << std::endl;
    std::cout << "z -> " << z << std::endl;
    std::cout << "****************************" << std::endl;

}

int main() {

    func();
    func(1);
    func(1, 2);
    func(1, 2, 3);

    return 0;
}

/*
    x -> 10
    y -> 20
    z -> 30
    ****************************
    x -> 1
    y -> 20
    z -> 30
    ****************************
    x -> 1
    y -> 2
    z -> 30
    ****************************
    x -> 1
    y -> 2
    z -> 3
    ****************************
 */
  • Varsayılan argüman konusunda yapılmaması gereken birkaç davranış var. Bunlar direk sentaks hatası oluyor zaten.

  • Fonksiyonun bir parametresi varsayılan argüman alıyorsa bundan sonra gelecek tüm parametreler varsayılan argüman almak zorunda. Yani sağ taraftaki tüm argümanlar.

  • Fonksiyon bildiriminde varsayılan değerler verildikten sonra fonksiyon tanımı yapılırken tekrardan değer verilmemelidir. Eğer fonksiyon bildirimi yok ise tanımlama da verilebilir.

C ve C++ Programlama Dilleri
+

Bu yazı C ve C++ programlama dilleri arasında ki farklılıklar, yeni standart ile gelen bazı değişiklikler vb konuları soru cevap şeklinde, maddeler olarak topladığım bir makaledir.

  • C++11 standartları ile beraber dil büyüklüğü neredeyse 2 katına çıktığını söyleyebiliriz. Bunun devamında C++14 standartları(minör) ve C++17 standartları(major) gelmiştir. C++20 standartları için çalışmalar devam etmektedir.

  • C++11 standartları ile dilin daha fazla genişlemesi konusunda birçok iyi ve kötü yorumlar yapıldı. Ama şu konu atlanıyor, Bjarne Stroustrup’un da yaptığı açıklamalarda C++ dilinin zaten küçük bir dil olma gibi bir amacı yoktur. Bu bakımdan C++11 standartlarıyla birlikte gelen yeniliklerle dil tamamen yenilendi. Hatta kendi tabirleri ile Modern C++ deniliyor.

  • C++ dilinin en övündüğü alanlardan başında Data Abstraction(Veri Soyutlama) gelmektedir.

  • C++ dili size uygulama yazarken birçok programlama tekniği ile geliştirme yapmanıza izin verir. (Nesne Yönelimli, Prosedürel, Çoklu Paradigmalı vs.)

  • C ve C++ dillerinde derleyici büyüklüğü zaten tartışılmaması gereken bir konudur. C dilinin amacı dili ve derleyiciyi olabildiğince küçük tutmaktır.

  • Peki bunu nasıl yapıyorlar? C derleyicisinin kontrolleri C++ derleyicileri kadar katı ve fazla değildir. Bu demek oluyorki, c programcıları kod yazarken hata yapma olasılıkları daha yüksektir. Derleyici kısmında ki kontrolleri azaltarak ve bazı mekanizmaları çıkartarak derleyiciyi küçük tutuyorlar.

  • C++ dili içerisinde ki C daha güvenlidir. Çünkü derleme zamanı kontrolleri daha fazladır.

  • C dilinde kullandığımız pointer kavrami C++ da yerlerini referanslara bırakıyor. (Yanlış anlaşılmasın pointer kavramı hala kullanılmaktadır. Referanslar sadece seviyesi yükseltilmiş notasyonel bir hiledir.)

  • C++ dilinde diziler çok nadir kullanılırlar. Bunun yerine vektor yada array sarmalayıcıları kullanılır. Örneğin C dilinde char str[100] yazı tutar ama C++ dilinde bunu kullanmayacağız. Char yerine string sınıfını kullanacağız. Bu cümleden şöyle bir anlam çıkarmayın! C++ dilinde char str[] kullanamıyoruz. Tabiki de kullanabiliyoruz fakat buna gerek yok.

C dilinden C++ diline geçerken performans kaybediyor muyuz?

  • Kesinliklikle PERFORMANS kaybetmiyoruz. Dilin geliştirilirken dikkat ettiği kısımlardan biri de PERFORMANS kaybetmemektir.

C ile C++ arasında ki bazı farklılıkları kod parçalarıyla inceleyelim.

func(int x){}

// implicit C, yani tür bilgisi yazmaya gerek yok.
// Derleyici bunu işaretli int olarak anlar.
// C++ dilinde bu kural IPTAL edilmiştir.

Geri dönüş değeri ile ilgili örnek

int func(){}
// C dilinde geçerlidir.
// C derleyicisi geri dönüş değeri üreteceğini söylemiştir.
// Return olmamasi geçerli.
// C++ dilinde geçersiz.

int func(int x){}
// C de geçerlidir.
// C++ dilinde geçersizdir.

int func(int){}
// C de isimlendirme mecburdur
// C++ dilinde isimlendirme mecbur değildir. Geçerlidir.

main fonksiyonunun geri dönüş değeri ve tipi

  • C dilinde neden main fonksiyonun geri dönüş değeri int ? Bunun sebebi exit fonksiyonuna yapılan çağrıdır. Non-zero başarısızlık, zeri ise başarı olarak kabul edilir. (Stdlib exit_clear, exit_success)

  • void main olarak tanımlasak ne olur? C dilinde main fonksiyonunun geri dönüş değerini void olarak kullanırsak programın başarıyla yada başarısızlık ile bittiği görülmeyecektir.

  • Diğer bir konu C dilinde main Recursive olarak çağırılabilir mi? C dilinde main Recursive olarak çağırılabilir. C++ dilinde ise bu yasaklanmıştır. C++ dilinde main fonksiyonuna return deyimi yazmaya gerek yoktur ama geliştirici olarak return yazın. Geri dönüş türü ise kesinlikle int olmalıdır.

KODU INCELEYELIM. C ve C++ derleyecileri bu koda nasıl bir davranış sergilerler

int main()
{
    func();
}

/* C++ kodu şöyle yorumlar
func benim için bir identifier(isim).
Ben bu ismi bir arayayım.(NAME LOOKUP)
Ismi bulamadığını söyler ve sentaks hatası verir.
Yani buradan çıkaracağımız C++ derleyicisi bir isim arayıp onu bulamaması direk sentaks hatası.
C dilinde ise bu GEÇERLIDIR.
*/

/* C kodu şöyle yorumlar
Bir fonksiyon bildirimi ile karşılasmasa bile önce func ismini arar.
Baktı ve bulamıyor. Sonra func fonksiyon çağrı operatörünün OPERANDI olmuş mu? diye bakar.
Evet olmuş. O zaman ben bu ismi arayıp bulamasam bile func benim için fonksiyon ismidir diyor.
Geri dönüş değeri int olan bir fonksiyon. (Yani implicit bir bildirim)
Bu aslında tehlikeli bir durum. C++ dilinde implicit function declaration yok.
C++ dilinde fonksiyon bildirimleri zorunludur.
*/

void func();
void foo(void);
// C++ dili için hiçbir anlam farkı yoktur.
// C dili için durum böyle değil. C dilinde foo fonksiyonunun parametre değişkeni yok demektir.
// func isimli fonksiyon ise bilgi vermiyorum demek. Yani değişkeni olmadığını değil bilgi vermediğini anlarız.
// PEKI BILGI VERMEMENIN ONEMI VAR MI?
// Tabiki de yok bu sadece GERIYE UYUMLULUGU sağlamak için yapılmıştır

Aşağıda ki kodu inceleyelim.

int func()
{
    printf("");
    int x;
    // executable statement
    // …
}
  • Kodun devamı var gibi düşünün. Şimdi C89 standartlarına göre kural şöyle yerel bildirimler blokların başında yapılmalıdır. Yani executable statement(yürütülebilir deyim)den sonra bildirim yapamayız. Bu kesinlikle kötü bir kural. Zaten bu kural konulduğuna da pişman olunulmuş sonradan.

  • Önce şu deyim mevzusunu hatırlayalım. C dilinde deyimler 2’ye ayrılıyordu. DECLARATION STATEMENT ve EXECUTABLE STATEMENT.

  • Şimdi OOP’de bu kural böyle olsaydı ileride çok büyük sıkıntılar çıkarabilirdi. Örnek olarak kodun bakımı.

  • Bir ismin scope nu ne kadar daraltırsak o kodun bakımını yapmak o kadar kolay olacaktır.

  • Konuya geri dönecek olursak artık yerel bildirimlerin blok başında yapılmasına gerek kalmadı. C++ dilinde artık gerekli yerlerde değişken tanımlaması yapılabilir. Böyle de yapılması gereklidir.

  • Burada diğer önemli bir konu ömür konusudur. Yerel değişkenler C de otomatik ömürlü de olabilir, statik ömürlüde olabilir. Statik ömürlü demek programın başından sonuna kadar bellek alanını korumasıdır.

  • Neler statik ömürlü hatırlayalım… Global değişkenler | Static keywordü ile tanımlananlar | Fonksiyonlar | Stringler.

  • Otomatik ömürlü nesnelere ilk değer vermezsek, garbage value ile hayata başlar. Çöp değerler hayata başlamanın zararı var mı? Eğer onu çöp değerle kullanırsak zararı var. Bu aslında bildirimin blok başında yapılmasının bir zorlaması. C++ da şuna alışmalıyız, eğer çok önemli bir nedeni yoksa tüm nesnelere ilk değer vererek hayata başlatın. Gene bu bildirim ile ilgili fazlaca kullandığımız bir yer daha var. for döngüleri, döngü değişkeni. Örneğin for(int k=0;….) C99 da bu eklenmiştir ve böyle kullanabiliyoruz. C++ da tabiki de bunu böyle kullanabiliyoruz. Java, C# gibi diller bu olayı C++ dan biraz daha önce görüp eklemişler. Şimdi bu kural olmasaydı örneğin C89 da kullanıyor gibi düşünelim, bize zararı nedir? Öncelikle scope unu gereksiz yere genişletiyor, aynı zamanda program sonuna kadar bu değişken visible durumda oluyor. Ama yeni kurala göre for parantezi içinde tanıtılan değişkenin ömrü döngünün bloğunun sonuna kadardır.

auto keywordü ile ilgili olarak C ve C++’da anlam değişimi var.

auto int x; // Bu kod C dilinde otomatik ömürlü demek

int x; // C++ dilinde bu da otomatik ömürlü, yani auto keywordunu kullanmaya gerek kalmıyor

auto x = 10; // işte burada bir anlam değişikliği var.
// auto burada değişkenin veri türünü belirliyor, yani burada int
  • Sonuç olarak auto artık C++11 standartlarında type detection. Yani tür bilgisi çıkarımını derleyici yapıyor.

C++11 decltype

decltype(exp) x; //x in türü exp neyse o

C89 standartlarında bool türü yok, C++ dilinde var

  • 0 = False ve 1 = True anlamına gelmektedir.

  • bool hemen hemen her sistem de 1 byte (char altı olacak)

  • C++ dilinde true ve false artık birer keyword

bool x = false; // yada true
  • C dilinde karşılaştırma türü int, C++ dilinde bool

  • bool ile diğer türler arası otomatik tür dönüşümü vardır.

bool x = 13; // non-zero bütün değerler True
  • Yani artık bool gereken yerler de direk olarak true false kullanmalıyız.

Karakter Sabiti Konusu

  • ‘A’ C dilinde int, C++ dilinde char
#include <iostream>

int main(){

    std::cout << typeid('A').name() << std::endl; // c

}
#include <iostream>
/*
 bool türü bir tam sayı türüdür. int altı türdür.
 C dilinde integral promotion kuralı, tam sayıya yükseltme kuralı int ten küçük türler işleme sokulduğunda int e dönüştürür.
 */
int main(){

    char c1 = 1, c2 = 3;
    std::cout << typeid(c1+c2).name(); // int

}

Veri Kaybı

double d = 34.7;
int ival = d; // d değeri 34
  • C ve C++ da legal bir davranış ama veri kaybı vardır. Bazı dillerde direk veri kaybına sebep olan davranışlar sentaks dışı yani direk hata.

  • C++11 de uniform initializer getirildi. Yani bunun anlamı veri kaybına yol açabilecek dönüşümleri geçersiz kılmak

#include <iostream>
int main() {
    // insert code here…

    double d = 34.6;
    int ival{d}; //Type double cannot be narrowed to int in initializer list

    return 0;
}

C ve C++ Dilinde Dönüşümler

  • C ve C++ dillerinde dönüşümler iki kategoriye ayrılıyor. IMPLICIT TYPE CONVERSION ve EXPLICIT TYPE CONVERSION

  • implicit dönüşüm otomatik dönüştürme. Şöyle anlatayım ival + dval olsun. Derleyici bunun için geçici bir nesne oluşturuyor. Bu işlemi double türünde yapıyor. Yani dönüştürme otomatik oluyor.

  • explicit ise operatör yardımıyla yapılan dönüşümlerdir. i1 ve i2 int türden değişkenler olsun. i1/i2 olarak bölme yapmamalıyız. int/int sonuç int çıkar. 10/3 = 3 olur. C dilinde tür dönüştürme operatürü (). Normalde olması gereken (double)i1/i2 … Ama artık C++ ta bunuda yapmamamız gerekiyor. Artık bunun için yeni 4 tane type cast operatörü geldi. Bu operatörler

static_cast
const_cast
reinterpret_cast
dynamic_cast
  • NULL C dilinde sembolik bir sabittir. C++ dilinde null var ama kesinlikle kullanmamalıyız. C++11 standartlarıyla yeni bir keyword eklendi. nullptr
ptr = 0;
// C ve C++ geçerli (ptr bir pointer olarak düşünelim)
// Otomatik olarak null dönüşümü yapıcak, bir pointera null atamak istediğimizde 0 yazabiliriz fakat C++11 de böyle yapmamalıyız.
  • C++11 de nullptr var. nullptr bir adres sabiti. Yani atama şu şekilde olmalı;
ptr = nullptr;
  • C++ dilinde mecbur kalmadıkça önişlemci komutlarını kullanmayacağız. Çünkü çok tehlikeli durumlar yaratabiliyor define komutu. Bunu ileri ki yazılarımda değinirim.

  • null sembolik sabitini bazi C derleyicileri (void*)0 olarak define etmiş.

int *ptr = (void*)0; // yanlış olur
// C++ dilinde void* türden T* türüne otomatik tür dönüşümü yok.

C++ dilinde ilk değer verme sentaksları;

int ival = 1;
int ival(1);
int ival = int();
int ival{exp};


char str[4] = "KAYA";
// C dilinde legal, C++ da error..
// C++ dilinde dizinin boyutu kadar karaktere sahip bir stringle ilk değer veremeyiz.
// initializer-string for char array is too long


char *p = "Murat";
*p = "S"; // Böyle birşey yapamayız, runtime hatası alırız. Stringler constant ve readonly dir

const char *p = "Murat"; // semantik açıdan bu daha doğru

// Yani stringleri asla char *p şeklinde göstermicez.

C++ dilinde pointerlar hala var ama C dilindeki kadar yoğun kullanmayacağız. C++ da seviyesi daha yüksek olan referansları kullanacağız.

C++ dilinde pointer ile primitive türler arası otomatik tur dönüşümü yok. C dilinde var.

int *p;
int x = 20;
p = x; // C dilinde yanlış ama legal, int türünden int * türüne otomatik tür dönüşümü gerçekleştirir

// C++ dilinde bu tamamen hata, tam sayı değerleri pointer değerlere atayamayız

// Farklı türden adres atamakta sentaks hatası

const int x; // C dilinde geçerli, C++ x değişkenine ilk değer vermek zorunlu
  • Sonuç olarak C++ dilinde pointer değişkenlere adres olmayan bir verinin atanması veya farklı türden bir adresin atanması sentaks hatası.