Bagikan melalui


Functions (C++)

Fungsi adalah blok kode yang melakukan beberapa operasi. Fungsi dapat secara opsional menentukan parameter input yang memungkinkan penelepon meneruskan argumen ke dalam fungsi. Fungsi dapat secara opsional mengembalikan nilai sebagai output. Fungsi berguna untuk merangkum operasi umum dalam satu blok yang dapat digunakan kembali, idealnya dengan nama yang dengan jelas menjelaskan apa yang dilakukan fungsi. Fungsi berikut menerima dua bilangan bulat dari penelepon dan mengembalikan jumlahnya; a dan b adalah parameter jenis int.

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

Fungsi ini dapat dipanggil, atau dipanggil, dari sejumlah tempat dalam program. Nilai yang diteruskan ke fungsi adalah argumen, yang jenisnya harus kompatibel dengan jenis parameter dalam definisi fungsi.

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

Tidak ada batas praktis untuk panjang fungsi, tetapi desain yang baik bertujuan untuk fungsi yang melakukan satu tugas yang terdefinisi dengan baik. Algoritma kompleks harus dipecah menjadi fungsi yang lebih mudah dipahami jika memungkinkan.

Fungsi yang ditentukan pada cakupan kelas disebut fungsi anggota. Di C++, tidak seperti bahasa lain, fungsi juga dapat didefinisikan pada cakupan namespace layanan (termasuk namespace layanan global implisit). Fungsi tersebut disebut fungsi bebas atau fungsi non-anggota; fungsi tersebut digunakan secara luas di Pustaka Standar.

Fungsi mungkin kelebihan beban, yang berarti versi fungsi yang berbeda dapat memiliki nama yang sama jika berbeda dengan jumlah dan/atau jenis parameter formal. Untuk informasi selengkapnya, lihat Kelebihan Beban Fungsi.

Bagian dari deklarasi fungsi

Deklarasi fungsi minimal terdiri dari jenis pengembalian, nama fungsi, dan daftar parameter (yang mungkin kosong), bersama dengan kata kunci opsional yang memberikan lebih banyak instruksi kepada pengkompilasi. Contoh berikut adalah deklarasi fungsi:

int sum(int a, int b);

Definisi fungsi terdiri dari deklarasi, ditambah isi, yang merupakan semua kode antara kurung kurawal:

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

Deklarasi fungsi diikuti oleh titik koma dapat muncul di beberapa tempat dalam suatu program. Ini harus muncul sebelum panggilan apa pun ke fungsi tersebut di setiap unit terjemahan. Definisi fungsi harus muncul hanya sekali dalam program, sesuai dengan Aturan Definisi Satu (ODR).

Bagian yang diperlukan dari deklarasi fungsi adalah:

  1. Jenis pengembalian, yang menentukan jenis nilai yang dikembalikan fungsi, atau void jika tidak ada nilai yang dikembalikan. Di C++11, auto adalah jenis pengembalian yang valid yang menginstruksikan pengkompilasi untuk menyimpulkan jenis dari pernyataan pengembalian. Di C++14, decltype(auto) juga diizinkan. Untuk informasi selengkapnya, lihat Jenis Pengurangan dalam Jenis Pengembalian di bawah ini.

  2. Nama fungsi, yang harus dimulai dengan huruf atau garis bawah dan tidak boleh berisi spasi. Secara umum, garis bawah terkemuka dalam nama fungsi Pustaka Standar menunjukkan fungsi anggota privat, atau fungsi non-anggota yang tidak dimaksudkan untuk digunakan oleh kode Anda.

  3. Daftar parameter, sekumpulan parameter nol atau lebih yang dipisahkan kurung kurawal, yang menentukan jenis dan secara opsional nama lokal tempat nilai dapat diakses di dalam isi fungsi.

Bagian opsional dari deklarasi fungsi adalah:

  1. constexpr, yang menunjukkan bahwa nilai pengembalian fungsi adalah nilai konstanta dapat dihitung pada waktu kompilasi.

    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. Spesifikasi tautannya, extern atau static.

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

    Untuk informasi selengkapnya, lihat Unit terjemahan dan linkage.

  3. inline, yang menginstruksikan pengkompilasi untuk mengganti setiap panggilan ke fungsi dengan kode fungsi itu sendiri. inlining dapat membantu performa dalam skenario di mana fungsi dijalankan dengan cepat dan dipanggil berulang kali di bagian kode kritis performa.

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

    Untuk informasi selengkapnya, lihat Fungsi Sebaris.

  4. Ekspresi noexcept , yang menentukan apakah fungsi dapat memberikan pengecualian atau tidak. Dalam contoh berikut, fungsi tidak melemparkan pengecualian jika is_pod ekspresi mengevaluasi ke true.

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

    Untuk informasi selengkapnya, lihat noexcept .

  5. (Hanya fungsi anggota) Kualifikasi cv, yang menentukan apakah fungsi tersebut const atau volatile.

  6. (Hanya fungsi anggota) virtual, override, atau final. virtual menentukan bahwa fungsi dapat ditimpa dalam kelas turunan. override berarti bahwa fungsi dalam kelas turunan mengambil alih fungsi virtual. final berarti fungsi tidak dapat ditimpa di kelas turunan lebih lanjut. Untuk informasi selengkapnya, lihat Fungsi Virtual.

  7. (hanya fungsi anggota) static diterapkan ke fungsi anggota berarti bahwa fungsi tidak terkait dengan instans objek kelas apa pun.

  8. (Hanya fungsi anggota non-statis) Ref-qualifier, yang menentukan ke pengkompilasi yang kelebihan beban fungsi untuk dipilih ketika parameter objek implisit (*this) adalah referensi rvalue vs. referensi lvalue. Untuk informasi selengkapnya, lihat Kelebihan Beban Fungsi.

Definisi fungsi

Definisi fungsi terdiri dari deklarasi dan isi fungsi, diapit kurung kurawal, yang berisi deklarasi variabel, pernyataan, dan ekspresi. Contoh berikut menunjukkan definisi fungsi lengkap:

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

Variabel yang dideklarasikan di dalam isi disebut variabel lokal atau lokal. Mereka keluar dari cakupan ketika fungsi keluar; oleh karena itu, fungsi tidak boleh mengembalikan referensi ke lokal!

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

fungsi const dan constexpr

Anda dapat mendeklarasikan fungsi anggota untuk const menentukan bahwa fungsi tidak diizinkan untuk mengubah nilai anggota data apa pun di kelas . Dengan mendeklarasikan fungsi anggota sebagai const, Anda membantu pengkompilasi untuk memberlakukan const-correctness. Jika seseorang salah mencoba memodifikasi objek dengan menggunakan fungsi yang dinyatakan sebagai const, kesalahan pengkompilasi dimunculkan. Untuk informasi selengkapnya, lihat const.

Mendeklarasikan fungsi seperti constexpr ketika nilai yang dihasilkannya mungkin dapat ditentukan pada waktu kompilasi. Fungsi constexpr umumnya dijalankan lebih cepat daripada fungsi reguler. Untuk informasi selengkapnya, lihat constexpr .

Templat Fungsi

Templat fungsi mirip dengan templat kelas; ini menghasilkan fungsi konkret berdasarkan argumen templat. Dalam banyak kasus, templat dapat menyimpulkan argumen jenis dan oleh karena itu tidak perlu menentukannya secara eksplisit.

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

Untuk informasi selengkapnya, lihat Templat Fungsi

Parameter dan argumen fungsi

Fungsi memiliki daftar parameter yang dipisahkan koma dari nol atau lebih jenis, yang masing-masing memiliki nama yang dapat diakses di dalam isi fungsi. Templat fungsi dapat menentukan lebih banyak jenis atau parameter nilai. Pemanggil meneruskan argumen, yang merupakan nilai konkret yang jenisnya kompatibel dengan daftar parameter.

Secara default, argumen diteruskan ke fungsi menurut nilai, yang berarti fungsi menerima salinan objek yang diteruskan. Untuk objek besar, membuat salinan bisa mahal dan tidak selalu diperlukan. Untuk menyebabkan argumen diteruskan oleh referensi (khususnya referensi lvalue), tambahkan kuantifier referensi ke parameter:

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

Saat fungsi memodifikasi argumen yang diteruskan oleh referensi, fungsi memodifikasi objek asli, bukan salinan lokal. Untuk mencegah fungsi mengubah argumen seperti itu, kualifikasi parameter sebagai const&:

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

C++11: Untuk secara eksplisit menangani argumen yang diteruskan oleh rvalue-reference atau lvalue-reference, gunakan double-ampersand pada parameter untuk menunjukkan referensi universal:

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

Fungsi yang dideklarasikan dengan kata kunci void tunggal dalam daftar deklarasi parameter tidak mengambil argumen, selama kata kunci void adalah anggota pertama dan satu-satunya dari daftar deklarasi argumen. Argumen jenis void di tempat lain dalam daftar menghasilkan kesalahan. Misalnya:

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

Meskipun ilegal untuk menentukan argumen kecuali seperti yang void diuraikan di sini, jenis yang berasal dari jenis void (seperti pointer ke void dan array void) dapat muncul di mana saja daftar deklarasi argumen.

Argumen Default

Parameter atau parameter terakhir dalam tanda tangan fungsi dapat diberi argumen default, yang berarti bahwa pemanggil dapat meninggalkan argumen saat memanggil fungsi kecuali mereka ingin menentukan beberapa nilai lain.

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)
{...}

Untuk informasi selengkapnya, lihat Argumen Default.

Jenis pengembalian fungsi

Fungsi mungkin tidak mengembalikan fungsi lain, atau array bawaan; namun dapat mengembalikan pointer ke jenis ini, atau lambda, yang menghasilkan objek fungsi. Kecuali untuk kasus ini, fungsi dapat mengembalikan nilai dari jenis apa pun yang berada dalam cakupan, atau mungkin tidak mengembalikan nilai, dalam hal ini jenis pengembalian adalah void.

Jenis pengembalian berikutnya

Jenis pengembalian "biasa" terletak di sisi kiri tanda tangan fungsi. Jenis pengembalian berikutnya terletak di sisi paling kanan tanda tangan dan didahului oleh -> operator. Jenis pengembalian berikutnya sangat berguna dalam templat fungsi ketika jenis nilai pengembalian tergantung pada parameter templat.

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

Ketika auto digunakan bersama dengan jenis pengembalian berikutnya, itu hanya berfungsi sebagai tempat penampung untuk apa pun yang dihasilkan ekspresi decltype, dan tidak dengan sendirinya melakukan pengurangan jenis.

Variabel lokal fungsi

Variabel yang dideklarasikan di dalam isi fungsi disebut variabel lokal atau hanya lokal. Lokal non-statis hanya terlihat di dalam isi fungsi dan, jika dinyatakan pada tumpukan keluar dari cakupan saat fungsi keluar. Saat Anda membuat variabel lokal dan mengembalikannya berdasarkan nilai, pengkompilasi biasanya dapat melakukan pengoptimalan nilai pengembalian bernama untuk menghindari operasi penyalinan yang tidak perlu. Jika Anda mengembalikan variabel lokal dengan referensi, pengkompilasi akan mengeluarkan peringatan karena upaya apa pun oleh pemanggil untuk menggunakan referensi tersebut akan terjadi setelah lokal dihancurkan.

Dalam C++ variabel lokal dapat dinyatakan sebagai statis. Variabel hanya terlihat di dalam isi fungsi, tetapi satu salinan variabel ada untuk semua instans fungsi. Objek statis lokal dihancurkan selama penghentian yang ditentukan oleh atexit. Jika objek statis tidak dibangun karena alur kontrol program melewati deklarasinya, tidak ada upaya yang dilakukan untuk menghancurkan objek tersebut.

Jenis pengurangan dalam jenis pengembalian (C++14)

Di C++14, Anda dapat menggunakan auto untuk menginstruksikan pengkompilasi untuk menyimpulkan jenis pengembalian dari isi fungsi tanpa harus memberikan jenis pengembalian berikutnya. Perhatikan bahwa auto selalu menyimpulkan ke nilai return-by-value. Gunakan auto&& untuk menginstruksikan pengkompilasi untuk menyimpulkan referensi.

Dalam contoh ini, auto akan disimpulkan sebagai salinan nilai non-const dari jumlah lh dan rhs.

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

Perhatikan bahwa auto tidak mempertahankan const-ness dari jenis yang disimpulkannya. Untuk fungsi penerusan yang nilai pengembaliannya perlu mempertahankan const-ness atau ref-ness argumennya, Anda dapat menggunakan decltype(auto) kata kunci, yang menggunakan decltype aturan inferensi jenis dan mempertahankan semua informasi jenis. decltype(auto) dapat digunakan sebagai nilai pengembalian biasa di sisi kiri, atau sebagai nilai pengembalian berikutnya.

Contoh berikut (berdasarkan kode dari N3493), menunjukkan decltype(auto) digunakan untuk mengaktifkan penerusan argumen fungsi yang sempurna dalam jenis pengembalian yang tidak diketahui sampai templat dibuat.

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());
}

Mengembalikan beberapa nilai dari fungsi

Ada berbagai cara untuk mengembalikan lebih dari satu nilai dari fungsi:

  1. Merangkum nilai dalam kelas bernama atau objek struct. Mengharuskan definisi kelas atau struktur terlihat oleh pemanggil:

    #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. Mengembalikan objek std::tuple atau std::p air:

    #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 versi 15.3 dan yang lebih baru (tersedia dalam /std:c++17 mode dan yang lebih baru): Gunakan pengikatan terstruktur. Keuntungan dari pengikatan terstruktur adalah bahwa variabel yang menyimpan nilai pengembalian diinisialisasi pada saat yang sama mereka dideklarasikan, yang dalam beberapa kasus dapat secara signifikan lebih efisien. Dalam pernyataan auto[x, y, z] = f(); , tanda kurung memperkenalkan dan menginisialisasi nama yang berada dalam cakupan untuk seluruh blok fungsi.

    #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. Selain menggunakan nilai pengembalian itu sendiri, Anda dapat "mengembalikan" nilai dengan menentukan sejumlah parameter untuk menggunakan referensi pass-by sehingga fungsi dapat memodifikasi atau menginisialisasi nilai objek yang disediakan pemanggil. Untuk informasi selengkapnya, lihat Argumen Fungsi Jenis Referensi.

Pointer fungsi

C++ mendukung penunjuk fungsi dengan cara yang sama dengan bahasa C. Namun alternatif yang lebih aman jenis biasanya menggunakan objek fungsi.

Disarankan agar Anda menggunakan typedef untuk mendeklarasikan alias untuk jenis penunjuk fungsi jika mendeklarasikan fungsi yang mengembalikan jenis penunjuk fungsi. Misalnya

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

Jika ini tidak dilakukan, sintaks yang tepat untuk deklarasi fungsi dapat disimpulkan dari sintaks deklarator untuk penunjuk fungsi dengan mengganti pengidentifikasi (fp dalam contoh di atas) dengan nama fungsi dan daftar argumen, sebagai berikut:

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

Deklarasi sebelumnya setara dengan deklarasi menggunakan typedef sebelumnya.

Baca juga

Kelebihan Beban Fungsi
Fungsi dengan Daftar Argumen Variabel
Fungsi Default dan Dihapus Secara Eksplisit
Pencarian Nama Dependen Argumen (Koenig) pada Fungsi
Argumen Default
Fungsi Sebaris