Thread overview
C++'da Referans/İşaretçi Türü ve Dinamik Bellek Kullanmadan Slicing Sorunundan Kurtulma
Aug 23, 2018
İbrahim
Aug 24, 2018
İbrahim
Aug 24, 2018
hsencan
Jan 16, 2019
Emre Kovancı
August 23, 2018

Selamün Aleyküm, hayırlı bayramlar;
Allah kurbanlarınızı kabul eylesin.

Bir önceki açtığım konumda sınıfların varsayılan olarak stack'de mi yoksa heap'de mi oluşturulması gerektiğini sormuştum. C++ harici çoğu dilin sınıfları stack'de değil de heap alanında oluşturmasının sebebinin slicing (dilimleme) sorununu ortadan kaldırmak olduğunu zannediyorum.

Bu sorunu C++ dilinde denemek için şu örneği hazırladım:

#include <iostream>
using namespace std;

class Database
{
protected:
 std::string db;

public:
 Database(const std::string& db) : db(db)
 {
 }

 virtual void display()
 {
   cout << "Database:" << endl
        << "  - Database Name: " << db << endl;
 }
};

class PostgreSQL : public Database
{
private:
 int db_version;

public:
 PostgreSQL(const std::string& db, int db_version)
   : Database(db), db_version(db_version)
 {
 }

 void display() override
 {
   cout << "PostgreSQL:" << endl
        << "  - Database Name: " << db << endl
        << "  - Database Version: " << db_version << endl;
 }
};

class MongoDB : public Database
{
private:
 int db_version;

public:
 MongoDB(const std::string& db, int db_version)
   : Database(db), db_version(db_version)
 {
 }

 void display() override
 {
   cout << "MongoDB:" << endl
        << "  - Database Name: " << db << endl
        << "  - Database Version: " << db_version << endl;
 }
};

void display(Database db /* Database& db */)
{
 db.display();
}

int main()
{
 PostgreSQL postgresql("PostgreSQL Example", 1);
 MongoDB mongodb("MongoDB Example", 2);

 display(postgresql);
 display(mongodb);

 return 0;
}

Bu örneği derleyip çalıştırdığımda şu çıktıyı elde ettim:

Database:
 1. Database Name: PostgreSQL Example
Database:
 1. Database Name: MongoDB Example

Normalde elde etmeye çalıştığımız çıktı şöyle olmalı:

PostgreSQL:
 1. Database Name: PostgreSQL Example
 2. Database Version: 1
MongoDB:
 1. Database Name: MongoDB Example
 2. Database Version: 2

İşte burada dilimleme - slicing sorunu ortaya çıkıyor. C++'da bunu yapmanın birkaç yolu var:

1- display fonksiyon parametresini referans olarak tanımlama:

void display(Database& db)
{
 db.display();
}

Fakat burada bir sorun daha ortaya çıkıyor ki o da geçici bir nesneyi argüman yapamıyoruz:

MongoDB mdb ("aaa", 1);
display(mdb); // TAMAM, Çalışıyor

display(MongoDB("aaa", 1)); // HATA, Derlenme hatası

2- display fonksiyon parametresini işaretçi yapmak:

void display(Database* db);

Lakin yine geçici bir nesneyi argüman olarak atayamıyoruz:

display(MongoDB("aaa", 1)); // HATA, Derleme hatası

3- Nesneyi statik değil, dinamik olarak oluşturmak (Heap Alanında):

void display(Database* db);

MongoDB* mdb = new MongoDB("aaa", 1);
display(mdb); // TAMAM, Çalışıyor.

Ama bunun da bellekten silinmesi gerekir ve çoğu zaman zahmetli. Ayrıca boyutları küçük olacak olan sınıf nesnelerini stack'de oluşturmak bence daha iyi.

Şimdi bu 3 yöntem de benim istediğim şey değil. Ben ise hem stack'de oluşan nesneyi kullanmak istiyorum:

MongoDB mdb("aaa", 1);

Hem geçici nesneyi kullanmak istiyorum:

display(MongoDB("aaa", 1));

Hem de işaretçi ve dinamik bellek yöntemini kullanmak istemiyorum.
Hem de bu kalıtım yöntemini bozmak istemiyorum :).

Bunun için nasıl bir yöntem izlemeliyim? display fonksiyonunda gönderilen argümanın türünün ne olduğuna bakarak birşeyler yapabilir miyim? Ya da ben imkansız olan bir şeyi mi istiyorum? :)

Teşekkürler!

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 24, 2018

Sağolun Ali Hocam. Bilmiyorum temiz bir yol mu lakin be işi şablon (template) kullanarak çözdüm:

template<typename T>
void display(T db)
{
 db.display();
}

..

PostgreSQL pdb("Db1", 1);
MongoDB mdb("Db2", 2);

display<PostgreSQL>(pdb);
display<MongoDB>(mdb);
display<PostgreSQL>(PostgreSQL("D3", 3));
display<MongoDB>(MongoDB("D4", 4));

Bu şablonlar yeri gelince kodu ne güzel süslüyorlar böyle + olarak çok işe yarıyorlar :)

Yalnız hocam, bu şablona malum parametre olarak herhangi bir tür verebiliriz, içerisinde display adlı bir üye fonksiyon barındırması kafi. Bu yüzden ben bu şablonun türünün (T) yalnızca Database sınıfı veya ondan türetilmiş sınıflar verilebileceğini, aksi halde içerisinde display() olsa dahi Database'den türemediği için başka bir türde hata vermesini koda anlatabilir miyim?

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 24, 2018

Alıntı (İbrahim):

>

bu şablonun türünün (T) yalnızca Database sınıfı veya ondan türetilmiş sınıflar verilebileceğini, aksi halde içerisinde display() olsa dahi Database'den türemediği için başka bir türde hata vermesini koda anlatabilir miyim?

Daha önce benim de ihtiyacım olmuştu. "std::enable_if" ile çözmüştüm. Anladığım kadarı ile senin istediğinde burada mevcut: https://stackoverflow.com/questions/42085326/c-template-function-for-derived-class-with-stdis-base-of

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 24, 2018

shared_ptr veya unique_ptr gibi işleyen bir tür tanımlayabilirsin (belki birileri tanımlamıştır). Ancak, bu nesne kurulurken ona "bunu new ile oluşturdum, lütfen delete et" diyebilmelisin. Sonuçta, bu işin temiz bir çözümü yok.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 25, 2018

Alıntı (İbrahim):

>

Yalnız hocam, bu şablona malum parametre olarak herhangi bir tür verebiliriz, içerisinde display adlı bir üye fonksiyon barındırması kafi.

Doğru. Kimi zaman çok yararlı kimi zaman da fazla esnek olduğu için zararlı kabul ediliyor.

Şablon, uyumsuz bir türle kullanıldığında derleme hatası şablonun içine işaret eder. Oysa, hatanın şablonu çağıran tarafta olduğunu bilmek isteriz. C++'a eklenmesi düşünülen concepts ve D'deki şablon kısıtlamaları bunun önüne geçmek için düşünülmüş.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

January 17, 2019

Alıntı (İbrahim:1535152040):

>

Yalnız hocam, bu şablona malum parametre olarak herhangi bir tür verebiliriz, içerisinde display adlı bir üye fonksiyon barındırması kafi. Bu yüzden ben bu şablonun türünün (T) yalnızca Database sınıfı veya ondan türetilmiş sınıflar verilebileceğini, aksi halde içerisinde display() olsa dahi Database'den türemediği için başka bir türde hata vermesini koda anlatabilir miyim?

konunun üzerinden zaman geçmiş ama daha sonra bakacak arkadaşlar için anahtar kelimemiz : SFINAE

https://www.bfilipek.com/2016/02/notes-on-c-sfinae.html
http://plepa.com/2016/10/27/enable_if/ // Sayın Necati Ergin'in konu üzerine türkçe içerikli paylaşımı

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]