Aracılığıyla paylaş


İşlevler [C++]

İşlev, bazı işlemler gerçekleştiren bir kod bloğudur. İşlev isteğe bağlı olarak çağıranların bağımsız değişkenleri işleve geçirmesini sağlayan giriş parametrelerini tanımlayabilir. İşlev isteğe bağlı olarak çıkış olarak bir değer döndürebilir. İşlevler, işlevin ne yaptığını açıkça açıklayan bir adla ideal olarak tek bir yeniden kullanılabilir blokta ortak işlemleri kapsüllemek için kullanışlıdır. Aşağıdaki işlev çağırandan iki tamsayı kabul eder ve toplamlarını döndürür; a ve b, türünde parametrelerdir.int

int sum(int a, int b)
{
    return a + b;
}

İşlev, programdaki herhangi bir sayıda yerden çağrılabilir veya çağrılabilir. İşleve geçirilen değerler, türleri işlev tanımındaki parametre türleriyle uyumlu olması gereken bağımsız değişkenlerdir.

int main()
{
    int i = sum(10, 32);
    int j = sum(i, 66);
    cout << "The value of j is" << j << endl; // 108
}

İşlev uzunluğu için pratik bir sınır yoktur, ancak iyi tasarım, iyi tanımlanmış tek bir görevi gerçekleştiren işlevleri hedefler. Karmaşık algoritmalar mümkün olduğunda anlaşılması kolay basit işlevlere ayrılmalıdır.

Sınıf kapsamında tanımlanan işlevlere üye işlevler adı verilir. C++ dilinde, diğer dillerden farklı olarak, bir işlev ad alanı kapsamında da tanımlanabilir (örtük genel ad alanı dahil). Bu tür işlevlere ücretsiz işlevler veya üye olmayan işlevler denir; Bunlar Standart Kitaplık'ta yaygın olarak kullanılır.

İşlevler aşırı yüklenmiş olabilir; başka bir deyişle, bir işlevin farklı sürümleri resmi parametrelerin sayısına ve/veya türüne göre farklıysa aynı adı paylaşabilir. Daha fazla bilgi için bkz . İşlev Aşırı Yükleme.

İşlev bildiriminin bölümleri

Minimal işlev bildirimi , derleyiciye daha fazla yönerge sağlayan isteğe bağlı anahtar sözcüklerin yanı sıra dönüş türünden, işlev adından ve parametre listesinden (boş olabilir) oluşur. Aşağıdaki örnek bir işlev bildirimidir:

int sum(int a, int b);

İşlev tanımı, bir bildirimin yanı sıra küme ayraçları arasındaki tüm kod olan gövdeden oluşur:

int sum(int a, int b)
{
    return a + b;
}

Bir işlev bildirimi ve ardından noktalı virgül bir programda birden çok yerde görünebilir. Her çeviri birimindeki bu işleve yapılan çağrıların öncesinde görünmelidir. İşlev tanımı, Tek Tanım Kuralına (ODR) göre programda yalnızca bir kez görünmelidir.

İşlev bildiriminin gerekli bölümleri şunlardır:

  1. İşlevin döndürdüğü değerin türünü belirten veya void değer döndürülmezse dönüş türü. C++11'de, auto derleyiciye dönüş deyiminden tür çıkarmasını belirten geçerli bir dönüş türüdür. C++14'te decltype(auto) de izin verilir. Daha fazla bilgi için aşağıdaki dönüş türlerinde Tür Kesintisi bölümüne bakın.

  2. Bir harf veya alt çizgiyle başlaması gereken ve boşluk içeremeyen işlev adı. Genel olarak, Standart Kitaplık işlev adlarındaki önde gelen alt çizgi, özel üye işlevlerini veya kodunuz tarafından kullanılmak üzere tasarlanmamış üye olmayan işlevleri gösterir.

  3. Parametre listesi, değerlerle işlev gövdesi içinde erişilebilen türü ve isteğe bağlı olarak yerel bir adı belirten, ayrılmış, virgülle ayrılmış bir sıfır veya daha fazla parametre kümesi.

İşlev bildiriminin isteğe bağlı bölümleri şunlardır:

  1. constexpr, işlevin dönüş değerinin sabit bir değer olduğunu gösterir ve derleme zamanında hesaplanabilir.

    constexpr float exp(float x, int n)
    {
        return n == 0 ? 1 :
            n % 2 == 0 ? exp(x * x, n / 2) :
            exp(x * x, (n - 1) / 2) * x;
    };
    
  2. Bağlantı belirtimi extern veya static.

    //Declare printf with C linkage.
    extern "C" int printf( const char *fmt, ... );
    
    

    Daha fazla bilgi için bkz . Çeviri birimleri ve bağlantı.

  3. inline, derleyiciye işleve yapılan her çağrıyı işlev kodunun kendisiyle değiştirmesini sağlar. inlining, bir işlevin hızla yürütüldüğü ve kodun performans açısından kritik bir bölümünde tekrar tekrar çağrıldığı senaryolarda performansa yardımcı olabilir.

    inline double Account::GetBalance()
    {
        return balance;
    }
    

    Daha fazla bilgi için bkz . Satır içi İşlevler.

  4. noexcept İşlevin özel durum oluşturup oluşturamayacağını belirten bir ifade. Aşağıdaki örnekte, ifade olarak değerlendirilirse is_podtrueişlev bir özel durum oluşturmaz.

    #include <type_traits>
    
    template <typename T>
    T copy_object(T& obj) noexcept(std::is_pod<T>) {...}
    

    Daha fazla bilgi için bkz. noexcept.

  5. (Yalnızca üye işlevleri) İşlevin const veya volatileolup olmadığını belirten cv-qualifiers.

  6. (Yalnızca üye işlevleri) virtual, overrideveya final. virtual bir işlevin türetilmiş bir sınıfta geçersiz kılınabileceğini belirtir. override türetilmiş bir sınıftaki bir işlevin bir sanal işlevi geçersiz kıldığını gösterir. final başka bir türetilmiş sınıfta bir işlevin geçersiz kılınamazsınız anlamına gelir. Daha fazla bilgi için bkz . Sanal İşlevler.

  7. (yalnızca üye işlevleri) static üye işlevine uygulandığında işlevin sınıfın hiçbir nesne örneğiyle ilişkili olmadığı anlamına gelir.

  8. (Yalnızca statik olmayan üye işlevleri) Örtük nesne parametresinin (*this) bir rvalue başvurusu ile lvalue başvurusu arasında ne zaman seçileceğini bir işlevin aşırı yüklemesini derleyiciye belirten başv niteleyicisi. Daha fazla bilgi için bkz . İşlev Aşırı Yükleme.

İşlev tanımları

İşlev tanımı , değişken bildirimleri, deyimleri ve ifadeleri içeren küme ayracı içine alınmış bildirim ve işlev gövdesinden oluşur. Aşağıdaki örnekte eksiksiz bir işlev tanımı gösterilmektedir:

    int foo(int i, std::string s)
    {
       int value {i};
       MyClass mc;
       if(strcmp(s, "default") != 0)
       {
            value = mc.do_something(i);
       }
       return value;
    }

Gövde içinde bildirilen değişkenler yerel değişkenler veya yerel değişkenler olarak adlandırılır. İşlevden çıkıldığında kapsam dışına çıkarlar; bu nedenle, bir işlev hiçbir zaman yerel bir başvuru döndürmemelidir!

    MyClass& boom(int i, std::string s)
    {
       int value {i};
       MyClass mc;
       mc.Initialize(i,s);
       return mc;
    }

const ve constexpr işlevleri

Bir üye işlevini, işlevin const sınıftaki veri üyelerinin değerlerini değiştirmesine izin verilmediğini belirtmek için olarak bildirebilirsiniz. Bir üye işlevini olarak constbildirerek, derleyicinin sabit doğruluğu zorlamasına yardımcı olursunuz. Birisi yanlışlıkla olarak constbildirilen bir işlevi kullanarak nesneyi değiştirmeye çalışırsa, derleyici hatası oluşur. Daha fazla bilgi için bkz . const.

Bir işlevi, ürettiği değerin derleme zamanında belirlenebileceği zaman olarak constexpr bildirin. Constexpr işlevi genellikle normal bir işlevden daha hızlı yürütülür. Daha fazla bilgi için bkz. constexpr.

İşlev Şablonları

İşlev şablonu, sınıf şablonuna benzer; şablon bağımsız değişkenlerini temel alan somut işlevler oluşturur. Çoğu durumda, şablon tür bağımsız değişkenlerini çıkarabildiği için bunları açıkça belirtmek gerekmez.

template<typename Lhs, typename Rhs>
auto Add2(const Lhs& lhs, const Rhs& rhs)
{
    return lhs + rhs;
}

auto a = Add2(3.13, 2.895); // a is a double
auto b = Add2(string{ "Hello" }, string{ " World" }); // b is a std::string

Daha fazla bilgi için bkz. İşlev Şablonları

İşlev parametreleri ve bağımsız değişkenleri

bir işlevin sıfır veya daha fazla türde virgülle ayrılmış parametre listesi vardır ve bunların her biri işlev gövdesi içinde erişilebileceği bir ada sahiptir. İşlev şablonu daha fazla tür veya değer parametresi belirtebilir. Çağıran, türleri parametre listesiyle uyumlu olan somut değerler olan bağımsız değişkenleri geçirir.

Varsayılan olarak, bağımsız değişkenler işleve değere göre geçirilir; başka bir deyişle işlev geçirilen nesnenin bir kopyasını alır. Büyük nesneler için, kopya oluşturmak pahalı olabilir ve her zaman gerekli değildir. Bağımsız değişkenlerin başvuru (özellikle lvalue başvurusu) tarafından geçirilmesine neden olmak için parametresine bir başvuru niceleyicisi ekleyin:

void DoSomething(std::string& input){...}

İşlev, başvuruyla geçirilen bir bağımsız değişkeni değiştirdiğinde, yerel kopyayı değil özgün nesneyi değiştirir. bir işlevin böyle bir bağımsız değişkeni değiştirmesini önlemek için parametreyi const olarak niteleyin::

void DoSomething(const std::string& input){...}

C++11: rvalue-reference veya lvalue-reference tarafından geçirilen bağımsız değişkenleri açıkça işlemek için, evrensel başvuru belirtmek için parametresinde bir çift ve işareti kullanın:

void DoSomething(const std::string&& input){...}

Parametre bildirimi listesindeki tek anahtar sözcükle void bildirilen bir işlev, bağımsız değişken bildirim listesinin void ilk ve tek üyesi olduğu sürece bağımsız değişken almaz. Listenin başka bir yerindeki türdeki void bağımsız değişkenler hata üretir. Örneğin:

// OK same as GetTickCount()
long GetTickCount( void );

Burada özetlenenler dışında bağımsız void değişken belirtmek geçersiz olsa da, türünden void türetilen türler (işaretçileri void ve dizileri voidgibi) bağımsız değişken bildirim listesinin herhangi bir yerinde görünebilir.

Varsayılan Bağımsız Değişkenler

İşlev imzasında son parametreye veya parametrelere varsayılan bir bağımsız değişken atanabilir; bu da çağıranın başka bir değer belirtmek istemediği sürece işlevi çağırırken bağımsız değişkenin dışında bırakabileceği anlamına gelir.

int DoSomething(int num,
    string str,
    Allocator& alloc = defaultAllocator)
{ ... }

// OK both parameters are at end
int DoSomethingElse(int num,
    string str = string{ "Working" },
    Allocator& alloc = defaultAllocator)
{ ... }

// C2548: 'DoMore': missing default parameter for parameter 2
int DoMore(int num = 5, // Not a trailing parameter!
    string str,
    Allocator& = defaultAllocator)
{...}

Daha fazla bilgi için bkz . Varsayılan Bağımsız Değişkenler.

İşlev dönüş türleri

İşlev başka bir işlev veya yerleşik dizi döndürmeyebilir; ancak bu türlere işaretçiler veya işlev nesnesi üreten bir lambda döndürebilir. Bu durumlar dışında, bir işlev kapsamdaki herhangi bir türde bir değer döndürebilir veya değer döndürmeyebilir; bu durumda dönüş türü olur void.

Sondaki dönüş türleri

İşlev imzasının sol tarafında "sıradan" bir dönüş türü bulunur. Sondaki dönüş türü , imzanın en sağ tarafında bulunur ve önünde -> işleç bulunur. Dönüş değerinin türü şablon parametrelerine bağlı olduğunda, son dönüş türleri işlev şablonlarında özellikle yararlıdır.

template<typename Lhs, typename Rhs>
auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs)
{
    return lhs + rhs;
}

auto Sondaki dönüş türüyle birlikte kullanıldığında, yalnızca decltype ifadesinin ürettiği her şey için bir yer tutucu işlevi görür ve tür kesintisi gerçekleştirmez.

İşlev yerel değişkenleri

İşlev gövdesi içinde bildirilen bir değişkene yerel değişken veya yalnızca yerel değişkenadı verilir. Statik olmayan yerel öğeler yalnızca işlev gövdesi içinde görünür ve yığında bildirilirse işlev çıktığında kapsam dışına çıkar. Yerel bir değişken oluşturup değere göre döndürdiğinizde, derleyici gereksiz kopyalama işlemlerinden kaçınmak için genellikle adlandırılmış dönüş değeri iyileştirmesini gerçekleştirebilir. Başvuruya göre bir yerel değişken döndürürseniz, çağıranın bu başvuruyu kullanma girişimi yerel yok edildikten sonra gerçekleşeceği için derleyici bir uyarı döndürür.

C++ dilinde yerel bir değişken statik olarak bildirilebilir. Değişken yalnızca işlev gövdesi içinde görünür, ancak işlevin tüm örnekleri için değişkenin tek bir kopyası vardır. Yerel statik nesneler atexit tarafından belirtilen sonlandırma sırasında yok edilir. Programın denetim akışı bildirimini atladığı için statik bir nesne oluşturmadıysa, bu nesneyi yok etmeye yönelik bir girişimde bulunulmamıştır.

Dönüş türlerinde tür kesintisi (C++14)

C++14'te, sonunda bir dönüş türü sağlamak zorunda kalmadan derleyiciye işlev gövdesinden dönüş türünü çıkarmasını bildirmek için komutunu kullanabilirsiniz auto . auto Her zaman bir değere dönüş olduğunu unutmayın. Derleyiciye başvuruyu çıkarmasını bildirmek için kullanın auto&& .

Bu örnekte, auto lhs ve rhs toplamının sabit olmayan bir değer kopyası olarak çıkarılacaktır.

template<typename Lhs, typename Rhs>
auto Add2(const Lhs& lhs, const Rhs& rhs)
{
    return lhs + rhs; //returns a non-const object by value
}

Bunun, çıkardığı auto türün sabitliğini korumadığını unutmayın. Dönüş değerinin bağımsız değişkenlerinin sabitliğini veya başvuru durumunu koruması gereken iletme işlevleri için, tür çıkarım kurallarını kullanan decltype ve tüm tür bilgilerini koruyan anahtar sözcüğünü kullanabilirsinizdecltype(auto). decltype(auto) sol tarafta normal bir dönüş değeri olarak veya sondaki dönüş değeri olarak kullanılabilir.

Aşağıdaki örnek (N3493'teki koda göre), şablon örneği alınana kadar bilinmeyen bir dönüş türünde işlev bağımsız değişkenlerinin mükemmel iletilmesine olanak tanımak için kullanıldığını gösterirdecltype(auto).

template<typename F, typename Tuple = tuple<T...>, int... I>
decltype(auto) apply_(F&& f, Tuple&& args, index_sequence<I...>)
{
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}

template<typename F, typename Tuple = tuple<T...>,
    typename Indices = make_index_sequence<tuple_size<Tuple>::value >>
   decltype( auto)
    apply(F&& f, Tuple&& args)
{
    return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices());
}

İşlevden birden çok değer döndürme

Bir işlevden birden fazla değer döndürmenin çeşitli yolları vardır:

  1. Adlandırılmış bir sınıf veya yapı nesnesindeki değerleri kapsülleyin. Sınıf veya yapı tanımının çağıranın görebilmesini gerektirir:

    #include <string>
    #include <iostream>
    
    using namespace std;
    
    struct S
    {
        string name;
        int num;
    };
    
    S g()
    {
        string t{ "hello" };
        int u{ 42 };
        return { t, u };
    }
    
    int main()
    {
        S s = g();
        cout << s.name << " " << s.num << endl;
        return 0;
    }
    
  2. bir std::tuple veya std::p air nesnesi döndür:

    #include <tuple>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    tuple<int, string, double> f()
    {
        int i{ 108 };
        string s{ "Some text" };
        double d{ .01 };
        return { i,s,d };
    }
    
    int main()
    {
        auto t = f();
        cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << endl;
    
        // --or--
    
        int myval;
        string myname;
        double mydecimal;
        tie(myval, myname, mydecimal) = f();
        cout << myval << " " << myname << " " << mydecimal << endl;
    
        return 0;
    }
    
  3. Visual Studio 2017 sürüm 15.3 ve üzeri (mod ve sonraki sürümlerde /std:c++17 kullanılabilir): Yapılandırılmış bağlamaları kullanın. Yapılandırılmış bağlamaların avantajı, dönüş değerlerini depolayan değişkenlerin bildirildikleri anda başlatılmasıdır ve bu da bazı durumlarda önemli ölçüde daha verimli olabilir. deyiminde auto[x, y, z] = f(); köşeli ayraçlar işlev bloğunun tamamı için kapsamdaki adları tanıtır ve başlatır.

    #include <tuple>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    tuple<int, string, double> f()
    {
        int i{ 108 };
        string s{ "Some text" };
        double d{ .01 };
        return { i,s,d };
    }
    struct S
    {
        string name;
        int num;
    };
    
    S g()
    {
        string t{ "hello" };
        int u{ 42 };
        return { t, u };
    }
    
    int main()
    {
        auto[x, y, z] = f(); // init from tuple
        cout << x << " " << y << " " << z << endl;
    
        auto[a, b] = g(); // init from POD struct
        cout << a << " " << b << endl;
        return 0;
    }
    
  4. Dönüş değerinin kendisini kullanmaya ek olarak, işlevin çağıranın sağladığı nesnelerin değerlerini değiştirebilmesi veya başlatabilmesi için geçiş başvurularını kullanmak üzere herhangi bir sayıda parametre tanımlayarak değerleri "döndürebilirsiniz". Daha fazla bilgi için bkz . Başvuru Türü İşlev Bağımsız Değişkenleri.

İşlev işaretçileri

C++ işlevi işaretçilerini C diliyle aynı şekilde destekler. Ancak daha güvenli bir alternatif genellikle bir işlev nesnesi kullanmaktır.

İşlev işaretçisi türü döndüren bir işlev bildiriyorsanız, işlev işaretçisi türü için bir diğer ad bildirmek için kullanmanız typedef önerilir. Örneğin:

typedef int (*fp)(int);
fp myFunction(char* s); // function returning function pointer

Bu yapılmazsa, tanımlayıcıyı (fp yukarıdaki örnekte) işlev adı ve bağımsız değişken listesiyle değiştirerek işlev bildirimi için doğru söz dizimi işlev işaretçisinin bildirimci söz diziminden aşağıdaki gibi çıkarılabilir:

int (*myFunction(char* s))(int);

Yukarıdaki bildirim, öncekini kullanan typedef bildirimle eşdeğerdir.

Ayrıca bkz.

İşlev Aşırı Yüklemesi
Değişken Bağımsız Değişken Listeleriyle İşlevler
Açıkça Varsayılan Haline Getirilen ve Silinen İşlevler
İşlevlerde Bağımsız Değişkene Bağlı Ad (Koenig) Arama
Varsayılan Bağımsız Değişkenler
Satır İçi İşlevler