Bagikan melalui


Alias dan typedefs (C++)

Anda dapat menggunakan deklarasi alias untuk mendeklarasikan nama yang akan digunakan sebagai sinonim untuk jenis yang dideklarasikan sebelumnya. (Mekanisme ini juga disebut secara informal sebagai alias jenis). Anda juga dapat menggunakan mekanisme ini untuk membuat templat alias, yang dapat berguna untuk alokator kustom.

Sintaks

using identifier = type;

Keterangan

identifier
Nama alias.

jenis
Pengidentifikasi jenis yang Anda buat aliasnya.

Alias tidak memperkenalkan jenis baru dan tidak dapat mengubah arti nama jenis yang ada.

Bentuk alias paling sederhana setara typedef dengan mekanisme dari C++03:

// C++11
using counter = long;

// C++03 equivalent:
// typedef long counter;

Kedua bentuk ini memungkinkan pembuatan variabel jenis counter. Sesuatu yang lebih berguna akan menjadi alias jenis seperti ini untuk std::ios_base::fmtflags:

// C++11
using fmtfl = std::ios_base::fmtflags;

// C++03 equivalent:
// typedef std::ios_base::fmtflags fmtfl;

fmtfl fl_orig = std::cout.flags();
fmtfl fl_hex = (fl_orig & ~std::cout.basefield) | std::cout.showbase | std::cout.hex;
// ...
std::cout.flags(fl_hex);

Alias juga berfungsi dengan penunjuk fungsi, tetapi jauh lebih mudah dibaca daripada typedef yang setara:

// C++11
using func = void(*)(int);

// C++03 equivalent:
// typedef void (*func)(int);

// func can be assigned to a function pointer value
void actual_function(int arg) { /* some code */ }
func fptr = &actual_function;

Batasan typedef mekanismenya adalah tidak berfungsi dengan templat. Namun, sintaks alias jenis di C++11 memungkinkan pembuatan templat alias:

template<typename T> using ptr = T*;

// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> ptr_int;

Contoh

Contoh berikut menunjukkan cara menggunakan templat alias dengan alokator kustom—dalam hal ini, jenis vektor bilangan bulat. Anda dapat mengganti jenis apa pun untuk int membuat alias yang nyaman untuk menyembunyikan daftar parameter kompleks dalam kode fungsi utama Anda. Dengan menggunakan alokator kustom di seluruh kode Anda, Anda dapat meningkatkan keterbacaan dan mengurangi risiko memperkenalkan bug yang disebabkan oleh kesalahan ketik.

#include <stdlib.h>
#include <new>

template <typename T> struct MyAlloc {
    typedef T value_type;

    MyAlloc() { }
    template <typename U> MyAlloc(const MyAlloc<U>&) { }

    bool operator==(const MyAlloc&) const { return true; }
    bool operator!=(const MyAlloc&) const { return false; }

    T * allocate(const size_t n) const {
        if (n == 0) {
            return nullptr;
        }

        if (n > static_cast<size_t>(-1) / sizeof(T)) {
            throw std::bad_array_new_length();
        }

        void * const pv = malloc(n * sizeof(T));

        if (!pv) {
            throw std::bad_alloc();
        }

        return static_cast<T *>(pv);
    }

    void deallocate(T * const p, size_t) const {
        free(p);
    }
};

#include <vector>
using MyIntVector = std::vector<int, MyAlloc<int>>;

#include <iostream>

int main ()
{
    MyIntVector foov = { 1701, 1764, 1664 };

    for (auto a: foov) std::cout << a << " ";
    std::cout << "\n";

    return 0;
}
1701 1764 1664

Typedefs

typedef Deklarasi memperkenalkan nama yang, dalam cakupannya, menjadi sinonim untuk jenis yang diberikan oleh bagian deklarasi jenis dari deklarasi.

Anda dapat menggunakan deklarasi typedef untuk membuat nama yang lebih pendek atau lebih bermakna untuk jenis yang sudah ditentukan oleh bahasa atau untuk jenis yang telah Anda deklarasikan. Nama typedef memungkinkan Anda untuk merangkum detail implementasi yang dapat berubah.

Berbeda dengan class, , structunion, dan enum deklarasi, typedef deklarasi tidak memperkenalkan jenis baru; mereka memperkenalkan nama baru untuk jenis yang ada.

Nama yang dinyatakan menggunakan typedef menempati namespace yang sama dengan pengidentifikasi lain (kecuali label pernyataan). Oleh karena itu, mereka tidak dapat menggunakan pengidentifikasi yang sama dengan nama yang dideklarasikan sebelumnya, kecuali dalam deklarasi jenis kelas. Pertimbangkan contoh berikut:

// typedef_names1.cpp
// C2377 expected
typedef unsigned long UL;   // Declare a typedef name, UL.
int UL;                     // C2377: redefined.

Aturan persembunyian nama yang berkaitan dengan pengidentifikasi lain juga mengatur visibilitas nama yang dideklarasikan menggunakan typedef. Oleh karena itu, contoh berikut adalah legal dalam C++:

// typedef_names2.cpp
typedef unsigned long UL;   // Declare a typedef name, UL
int main()
{
   unsigned int UL;   // Redeclaration hides typedef name
}

// typedef UL back in scope

Contoh lain dari persembunyian nama:

// typedef_specifier1.cpp
typedef char FlagType;

int main()
{
}

void myproc( int )
{
    int FlagType;
}

Saat Anda mendeklarasikan pengidentifikasi cakupan lokal dengan nama yang sama dengan typedef, atau ketika Anda mendeklarasikan anggota struktur atau gabungan dalam cakupan yang sama atau dalam cakupan dalam, penentu jenis harus ditentukan. Contohnya:

typedef char FlagType;
const FlagType x;

Untuk menggunakan kembali nama FlagType sebagai pengidentifikasi, anggota struktur, atau anggota gabungan, tipenya harus disediakan:

const int FlagType;  // Type specifier required

Tidak cukup untuk mengatakan

const FlagType;      // Incomplete specification

FlagType karena diambil untuk menjadi bagian dari jenis , bukan pengidentifikasi yang sedang dideklarasikan ulang. Deklarasi ini diambil untuk menjadi deklarasi ilegal, mirip dengan:

int;  // Illegal declaration

Anda dapat mendeklarasikan jenis apa pun dengan typedef, termasuk jenis pointer, fungsi, dan array. Anda dapat mendeklarasikan nama typedef untuk sebuah penunjuk ke tipe struktur atau gabungan sebelum Anda mendefinisikan tipe struktur atau gabungan, selama definisi tersebut memiliki visibilitas yang sama dengan deklarasi.

Contoh

Salah satu penggunaan typedef deklarasi adalah membuat deklarasi lebih seragam dan ringkas. Contohnya:

typedef char CHAR;          // Character type.
typedef CHAR * PSTR;        // Pointer to a string (char *).
PSTR strchr( PSTR source, CHAR target );
typedef unsigned long ulong;
ulong ul;     // Equivalent to "unsigned long ul;"

Untuk digunakan typedef untuk menentukan jenis dasar dan turunan dalam deklarasi yang sama, Anda dapat memisahkan deklarator dengan koma. Contohnya:

typedef char CHAR, *PSTR;

Contoh berikut menyediakan jenis DRAWF untuk fungsi yang tidak mengembalikan nilai dan mengambil dua argumen int:

typedef void DRAWF( int, int );

Setelah pernyataan di atas typedef , deklarasi

DRAWF box;

akan setara dengan deklarasi

void box( int, int );

typedef sering dikombinasikan dengan struct untuk mendeklarasikan dan memberi nama jenis yang ditentukan pengguna:

// typedef_specifier2.cpp
#include <stdio.h>

typedef struct mystructtag
{
    int   i;
    double f;
} mystruct;

int main()
{
    mystruct ms;
    ms.i = 10;
    ms.f = 0.99;
    printf_s("%d   %f\n", ms.i, ms.f);
}
10   0.990000

Redeclaration typedefs

typedef Deklarasi dapat digunakan untuk mendeklarasikan ulang nama yang sama untuk merujuk ke jenis yang sama. Contohnya:

File sumber file1.h:

// file1.h
typedef char CHAR;

File sumber file2.h:

// file2.h
typedef char CHAR;

File sumber prog.cpp:

// prog.cpp
#include "file1.h"
#include "file2.h"   // OK

File prog.cpp menyertakan dua file header, yang keduanya berisi typedef deklarasi untuk nama CHAR. Selama kedua deklarasi mengacu pada jenis yang sama, redeklarasi tersebut dapat diterima.

typedef Tidak dapat menentukan ulang nama yang sebelumnya dinyatakan sebagai jenis yang berbeda. Pertimbangkan alternatif file2.hini :

// file2.h
typedef int CHAR;     // Error

Pengkompilasi mengeluarkan kesalahan karena prog.cpp upaya untuk mendeklarasi ulang nama CHAR untuk merujuk ke jenis yang berbeda. Kebijakan ini meluas ke konstruksi seperti:

typedef char CHAR;
typedef CHAR CHAR;      // OK: redeclared as same type

typedef union REGS      // OK: name REGS redeclared
{                       //  by typedef name with the
    struct wordregs x;  //  same meaning.
    struct byteregs h;
} REGS;

typedefs di C++ vs. C

Penggunaan penentu typedef dengan jenis kelas sebagian besar didukung karena praktik ANSI C mendeklarasikan struktur yang tidak disebutkan namanya dalam typedef deklarasi. Misalnya, banyak pemrogram C menggunakan idiom berikut:

// typedef_with_class_types1.cpp
// compile with: /c
typedef struct {   // Declare an unnamed structure and give it the
                   // typedef name POINT.
   unsigned x;
   unsigned y;
} POINT;

Keuntungan dari deklarasi seperti itu adalah memungkinkan deklarasi seperti:

POINT ptOrigin;

Melainkan:

struct point_t ptOrigin;

Di C++, perbedaan antara typedef nama dan jenis nyata (dinyatakan dengan classkata kunci , , unionstruct, dan enum ) lebih berbeda. Meskipun praktik C mendeklarasikan struktur tanpa nama dalam pernyataan typedef masih berfungsi, itu tidak memberikan manfaat notasi seperti yang dilakukan dalam C.

// typedef_with_class_types2.cpp
// compile with: /c /W1
typedef struct {
   int POINT();
   unsigned x;
   unsigned y;
} POINT;

Contoh sebelumnya mendeklarasikan kelas bernama POINT menggunakan sintaks kelas typedef yang tidak disebutkan namanya. POINT diperlakukan sebagai nama kelas; namun, pembatasan berikut berlaku untuk nama yang diperkenalkan dengan cara ini:

  • Nama (sinonim) tidak dapat muncul setelah awalan class, , structatau union .

  • Nama tidak dapat digunakan sebagai nama konstruktor atau destruktor dalam deklarasi kelas.

Singkatnya, sintaks ini tidak menyediakan mekanisme apa pun untuk pewarisan, konstruksi, atau penghancuran.