std::function ve std::bind
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 86 / 104.
Lambda'lar güçlü olsa da bazen farklı türlere sahip çağrılabilir nesneleri tekdüze bir şekilde depolamanız veya mevcut işlevleri gereken imzayla eşleşecek şekilde uyarlamanız gerekir. std::function ve std::bind, <functional> başlığından bu sorunları çözer.
std::function, belirli bir imzayla eşleşen herhangi bir çağrılabilir öğeyi (fonksiyonlar, lambda'lar veya fonksiyon nesneleri) tutabilen, tür silme kullanan bir sarmalayıcıdır:
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
std::function<int(int, int)> operation;
operation = add; // Normal fonksiyon
std::cout << operation(3, 4) << "\n"; // 7
operation = [](int a, int b) { return a * b; }; // Lambda
std::cout << operation(3, 4) << "\n"; // 12
}std::bind, mevcut bir işlevin bazı bağımsız değişkenlerini sabitleyerek yeni bir çağrılabilir oluşturur. Değişken kalacak bağımsız değişkenleri işaretlemek için std::placeholders::_1, _2 vb. kullanın:
#include <iostream>
#include <functional>
void greet(const std::string& greeting, const std::string& name) {
std::cout << greeting << ", " << name << "!\n";
}
int main() {
using namespace std::placeholders;
auto sayHello = std::bind(greet, "Hello", _1);
sayHello("Alice"); // Merhaba, Alice!
auto swapped = std::bind(greet, _2, _1);
swapped("Bob", "Hi"); // Selam, Bob!
}Bu araçlar, geri çağırmaları sınıf üyeleri olarak depolamak veya Strateji desenini uygulamak için OOP'de özellikle kullanışlıdır. Ancak modern C++ daha iyi okunabilirlik ve performans için genellikle std::bind yerine lambda ifadelerini tercih eder; std::bindi esas olarak bağımsız değişkenlerin sırasını değiştirmeniz veya eski kodla çalışmanız gerektiğinde kullanın.
Görev
Kolaystd::function ve std::bind gücünü Demonstrate eden yapılandırılabilir bir calculator oluşturalım. Matematiksel işlemlerin çalışma zamanında depolanabildiği, değiştirilebildiği ve özelleştirilebildiği bir sistem oluşturacaksın: bu araçların esnek callback yönetimini nasıl mümkün kıldığını göreceksin.
Codenu üç dosyada düzenleyeceksin:
MathOperations.h: İşlem kitaplığın olarak hizmet edecek bağımsız matematik function koleksiyonu Declare et.Bu functionları Create et:
add(int a, int b): toplamı döndürürsubtract(int a, int b): difference döndürür (a - b)multiply(int a, int b): çarpımı döndürürpower(int base, int exponent, int multiplier):multiplier * (base ^ exponent)döndürür. Üssü hesaplamak için basit bir döngü kullan (exponent değerlerinin negatif olmadığını assume et).
Calculator.h: İşlemleri dinamik olarak depolamak ve Execute etmek içinstd::functionkullanan birCalculatorclassı define et.Calculator'ın şunlara sahip olmalı:
- current ikili işlemi depolamak için private bir
std::function<int(int, int)>üye setOperation(std::function<int(int, int)> op): current işlemi ayarlarcalculate(int a, int b): depolanan işlemi Executes eder ve sonucu döndürür
Gerekli başlıkları (
<functional>) include et ve MathOperations başlığını include ettiğinden emin ol.- current ikili işlemi depolamak için private bir
main.cpp: Üç input oku:- İşlem adı:
add,subtract,multiplyveyasquare - first sayı (integer)
- İkinci sayı (integer)
Bir
CalculatorCreate et ve işlem adına göre Configure et:add,subtractvemultiplyiçin: corresponding functionı directly calculator'a assign etsquareiçin: her zaman exponent olarak 2 ve multiplier olarak 1 kullanan özelleştirilmiş birpowersürümü Create etmek içinstd::bindkullan. Bound function iki argument kabul etmeli; bunlardan yalnızca first olan base olarak kullanılmalı (ikinci argument bir placeholder kullanılarak yok sayılabilir).
İşlemi ayarladıktan sonra iki sayınla
calculate()Call et ve şunu yazdır:Result: [value]Ardından calculator'a
a + b + 100döndüren bir lambda assign ederek, aynı inputlerle çalıştırıp şunu yazdırarak işlemleri değiştirmeyi Demonstrate et:With bonus: [value]- İşlem adı:
Örneğin inputler add, 10 ve 5 olduğunda:
Result: 15
With bonus: 115Inputler multiply, 7 ve 3 olduğunda:
Result: 21
With bonus: 110Inputler square, 4 ve 0 olduğunda:
Result: 16
With bonus: 104Bu challenge, std::function'ın farklı çağrılabilir türleri (normal functionlar, bound functionlar ve lambdalar) depolamak için nasıl uniform bir yol sağladığını; std::bind'in ise bazı argumentlarını sabitleyerek mevcut functionları uyarlamana nasıl olanak tanıdığını gösterir.
Kendin dene
#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"
using namespace std;
int main() {
// Girdileri oku
string operation;
int num1, num2;
cin >> operation >> num1 >> num2;
// Bir Calculator örneği oluştur
Calculator calc;
// TODO: Hesap makinesini işlem adına göre yapılandır
// "add", "subtract", "multiply" için: ilgili fonksiyonu doğrudan ata
// "square" için: power'ın özelleştirilmiş bir sürümünü oluşturmak için std::bind kullan
// her zaman üs 2 ve çarpan 1 kullanan
// İpucu: Taban argümanı için std::placeholders::_1 kullan
if (operation == "add") {
// Kodunuz buraya
} else if (operation == "subtract") {
// Kodunuz buraya
} else if (operation == "multiply") {
// Kodunuz buraya
} else if (operation == "square") {
// Kodunuz buraya - power fonksiyonu ile std::bind kullan
}
// TODO: calculate() çağır ve sonucu yazdır
// Biçim: "Result: [value]"
// TODO: İşlem değiştirmeyi göster
// Hesap makinesine a + b + 100 döndüren bir lambda ata
// calculate() fonksiyonunu tekrar çağır ve sonucu yazdır
// Biçim: "With bonus: [value]"
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner12Modern C++ Özellikleri
Taşıma Semantiği ve R-değerleriMükemmel YönlendirmeDerinlemesine Lambda İfadeleristd::function ve std::bindconstexpr ve constevalYapılandırılmış Bağlamalaroptional, variant, anyKendi başına pratik yap: Online C++ derleyicisi