Struct'larda Fonksiyon İşaretçileri
Coddy'nin C Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 38 / 61.
Şimdiye kadar, fonksiyon göstericilerini bağımsız değişkenler olarak kullandık veya argüman olarak geçirdik. Polimorfizme doğru bir sonraki adım, bir fonksiyon göstericisini doğrudan bir struct içine yerleştirmektir — bu da struct'a kendi davranışını kazandırır.
Bir struct bir fonksiyon göstericisi (function pointer) içerdiğinde, her bir örnek farklı bir fonksiyon tutabilir. Bu, aynı türdeki iki nesnenin, fonksiyon göstericilerini çağırdığınızda farklı davranabileceği anlamına gelir:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Bir üye olarak fonksiyon göstericisi
} Printer;Printer yapısı artık iki üyeye sahiptir: veri (name) ve davranış (print). Bunu kullanmak için, başlatma sırasında işaretçiye bir fonksiyon atarsınız:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Çıktı: !!! Hello !!!
return 0;
}Fonksiyonu struct üzerinden nasıl çağırdığımıza dikkat edin: p.print("Hello"). Bu, diğer dillerdeki bir nesne üzerinde metot çağırmaya oldukça benzer görünür. Struct, hem verilerini hem de bu veriler üzerinde işlem yapan fonksiyonu taşır.
Bu desen, C'deki polimorfizmin temelidir — aynı struct türünden farklı davranışlar üreterek, farklı örneklere farklı fonksiyonlar atanabilir.
Görev
KolayStruct'ların fonksiyon göstericileri (function pointers) aracılığıyla kendi davranışlarını nasıl taşıyabildiğini gösteren bir Notifier sistemi kuralım.
Kodunuzu düzenlemek için iki dosya oluşturacaksınız:
notifier.h: Birconst char*parametresi alan ve hiçbir şey döndürmeyenNotifyFuncadında bir fonksiyon göstericisi türü tanımlayın. Ardından, birname(birconst char*) veNotifyFunctüründe birnotifyfonksiyon göstericisi içeren birNotifierstruct'ı tanımlayın.main.c: Başlık dosyanızı dahil edin ve iki bildirim fonksiyonu uygulayın:alert_notify— mesajı şu formatta yazdırır:[ALERT] messageinfo_notify— mesajı şu formatta yazdırır:[INFO] message
Notifierörneği oluşturun, girişe göre uygun fonksiyonu bağlayın ve bildirimi struct'ın fonksiyon göstericisi üzerinden çağırın.
Programınız iki giriş alacaktır: bir bildirim türü (alert için 1, info için 2) ve görüntülenecek bir mesaj.
Türe bağlı olarak, ilgili fonksiyonu notifier'ınızın notify üyesine atayın, ardından mesajı görüntülemek için struct üzerinden çağırın.
Tür 1 ve mesaj "System starting" olduğunda örnek çıktı:
[ALERT] System startingTür 2 ve mesaj "All systems normal" olduğunda örnek çıktı:
[INFO] All systems normalBaşlık dosyanızda include guard kullanmayı ve fonksiyonu struct üyesi üzerinden çağırmayı unutmayın: n.notify(message)
Kopya kağıdı
Fonksiyon göstericileri doğrudan yapıların (structs) içine gömülebilir, bu da her örneğe kendi davranışını kazandırır. Bu, C'de polimorfizmin (çok biçimlilik) temelidir.
Fonksiyon Göstericisi İçeren Bir Yapı Tanımlama
Öncelikle bir fonksiyon göstericisi türü tanımlayın, ardından bunu bir yapı üyesi olarak ekleyin:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Bir üye olarak fonksiyon göstericisi
} Printer;Yapıyı Kullanma
Başlatma sırasında göstericiye bir fonksiyon atayın, ardından bu fonksiyonu yapı üzerinden çağırın:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Çıktı: !!! Hello !!!
return 0;
}p.print("Hello") sözdizimi, diğer dillerdeki bir nesne üzerinde metot çağırmaya benzer şekilde, fonksiyonu yapı üzerinden çağırır.
Polimorfik Davranış
Aynı yapı türünün farklı örneklerine farklı fonksiyonlar atanabilir, bu da aynı yapı türünden farklı davranışlar üretilmesini sağlar.
Kendin dene
#include <stdio.h>
#include "notifier.h"
// TODO: alert_notify fonksiyonunu uygula
// Şunu yazdırmalı: [ALERT] message
// TODO: info_notify fonksiyonunu uygula
// Şunu yazdırmalı: [INFO] message
int main() {
int type;
char message[256];
// Girdiyi oku
scanf("%d", &type);
getchar(); // yeni satır karakterini tüket
fgets(message, sizeof(message), stdin);
// Eğer varsa, mesajın sonundaki yeni satır karakterini kaldır
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Bir Notifier örneği oluştur
// TODO: type değerine göre (alert için 1, info için 2),
// uygun fonksiyonu notifier'ın notify üyesine ata
// TODO: struct'ın fonksiyon göstericisi (function pointer) aracılığıyla bildirimi çağır
// Kullanım: n.notify(message)
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1Modüler Programlama Temelleri
Başlık DosyalarıInclude Guard'larKaynak DosyalarıStatik FonksiyonlarÖzet: Modüler Hesap Makinesi4Kapsülleme
Opaque Pointer KavramıOpaque Struct TanımlamaGetter ve Setter MetotlarıSetter Metotlarında DoğrulamaÖzet: Gizli Kutu2Nesneler ve Metotlar
Nesne Olarak Struct'lar'Self' İşaretçisiConst Doğruluğuİşaretçi vs DeğerYardımcı MetotlarÖzet: Point Manager5Proje: Basit Banka Hesabı
Proje KurulumuHesabın Oluşturulması8Polimorfizm
Struct'larda Fonksiyon İşaretçileriMetotları Simüle EtmeArayüz KavramıArayüzleri UygulamaPolimorfik İterasyonÖzet: Greeter3Nesne Yaşam Döngüsü
Constructor DeseniDestructor DeseniStack İlklendirmeDerin KopyalamaÖzet: String Wrapper6Kompozisyon Yoluyla Kalıtım
Struct Gömmeİlk Üye KuralıÜst Üyelere ErişimUpcastingÖzet: Şekil Hiyerarşisi