Sampai maut memisahkan kita atau semua tentang statis di C ++





Halo. Pada salah satu review kode, saya menemukan ide yang banyak, dan apa yang harus disembunyikan, bukan bahwa kita memahami dengan baik kapan harus menggunakan kata kunci statis . Pada artikel kali ini, saya ingin berbagi pengetahuan dan informasi saya mengenai kata kunci statis . , , ++. /++ . , static ++, . ++, , , , .



static?



Statis adalah kata kunci C ++ yang digunakan untuk memberikan karakteristik khusus pada elemen. Untuk elemen statis, memori dialokasikan hanya sekali dan elemen ini ada hingga program berakhir. Semua elemen ini disimpan tidak dalam tumpukan atau di stack, tetapi dalam segmen memori khusus yang disebut DATA dan .bss (tergantung pada apakah data statis diinisialisasi atau tidak). Gambar di bawah ini menunjukkan tata letak khas untuk memori program.





Dimana ini digunakan?



Di bawah ini adalah diagram bagaimana dan di mana statik digunakan dalam suatu program.







Dan sekarang saya akan mencoba menjelaskan secara rinci semua yang ditunjukkan dalam diagram. Pergilah!



Variabel statis di dalam fungsi



Variabel statis, ketika digunakan di dalam fungsi, diinisialisasi hanya sekali, dan kemudian mereka mempertahankan nilainya. Variabel statis ini disimpan di area memori statis ( .data atau .bss ) daripada di tumpukan, yang memungkinkan nilai variabel disimpan dan digunakan selama program berjalan. Mari kita lihat dua program yang hampir identik dan perilakunya. Satu-satunya perbedaan adalah bahwa yang satu menggunakan variabel statis, dan yang lainnya tidak.



Program pertama:



#include <iostream>

void counter() {
  static int count = 0; //  4
  std::cout << count++;
}

int main() {
  for (int i = 0; i < 10; ++i) {
    counter();
  }
  return 0;
}


Keluaran program:

0123456789


Program kedua:



#include <iostream>

void counter() {
  int count = 0; //  4
  std::cout << count++;
}

int main() {
  for (int i = 0; i < 10; ++i) {
    counter();
  }
  return 0;
}


Keluaran program:

000000000


Jika Anda tidak menggunakan statis pada baris 4 , alokasi memori dan inisialisasi jumlah variabel terjadi setiap penghitung waktu () dipanggil , dan dimusnahkan setiap kali fungsi keluar. Tetapi jika kita membuat variabel menjadi statis, setelah inisialisasi (pertama kali fungsi counter () dipanggil ), hitungan akan dibatasi hingga akhir fungsi main () , dan variabel akan mempertahankan nilainya di antara panggilan ke counter () .



Objek kelas statis



Objek statis kelas memiliki properti yang sama dengan variabel statis biasa yang dijelaskan di atas, yaitu. disimpan dalam segmen memori .data atau .bss , dibuat saat startup dan dimusnahkan saat program dihentikan, dan diinisialisasi hanya sekali. Objek diinisialisasi seperti biasa - melalui konstruktor kelas. Mari pertimbangkan contoh dengan objek kelas statis.



#include <iostream>

class Base { //  3
public:
  Base() { //  5
    std::cout << "Constructor" << std::endl;
  }
  ~Base() { //  8
    std::cout << "Destructor" << std::endl; 
  }
};

void foo() { 
  static Base obj; //  14
} //  15

int main() {
  foo(); //  18
  std::cout << "End of main()" << std::endl;
  return 0;
}


Keluaran program:

Pembuat

Akhir dari main ()

Destruktor


3 Base ( 5) ( 8). . 14 obj Base. foo() 18.



- , , foo() 15, , .. . , , .





#include <iostream>

class Base {
public:
  Base() {
    std::cout << "Constructor" << std::endl;
  }
  ~Base() { 
    std::cout << "Destructor" << std::endl; 
  }
};

void foo() { 
  Base obj; 
} //  15

int main() {
  foo();
  std::cout << "End of main()" << std::endl;
  return 0;
}


Jika kita menghapus statis saat membuat variabel di fungsi foo () , maka penghancuran objek akan terjadi di baris 15 setiap kali fungsi dipanggil. Dalam hal ini, keluaran program akan sangat diharapkan untuk variabel lokal dengan memori yang dialokasikan pada tumpukan:

Constructor

Destructor

End of main ()




Anggota kelas statis



Dibandingkan dengan kasus penggunaan sebelumnya, anggota statis kelas sedikit lebih sulit untuk dipahami. Mari kita lihat alasannya. Misalkan kita memiliki program berikut:



#include <iostream>

class A { //  3
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B { //  9
public:
  B() { std::cout << "Constructor B" << std::endl; }
  ~B() { std::cout << "Destructor B" << std::endl; }

private:
  static A a; //  15 ()
};

int main() {
  B b; //  19
  return 0;
}


Dalam contoh kami, kami membuat kelas A (baris 3) dan kelas B (baris 9) dengan anggota kelas statis ( baris 15 ). Kami berasumsi bahwa membuat objek b pada baris 19 akan membuat objek a pada baris 15 . Ini akan menjadi kasus jika kita menggunakan anggota kelas non-statis. Tetapi output dari program ini adalah sebagai berikut:

Pembuat B

Perusak B


Alasan untuk perilaku ini adalah bahwa anggota statis kelas tidak diinisialisasi dengan konstruktor karena tidak bergantung pada inisialisasi objek. Itu. pada baris 15, kita hanya mendeklarasikan objek, bukan mendefinisikannya, karena definisi harus terjadi di luar kelas menggunakan operator resolusi cakupan (: :) . Mari kita mendefinisikan sebuah anggota kelas B .



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B {
public:
  B() { std::cout << "Constructor B" << std::endl; }
  ~B() { std::cout << "Destructor B" << std::endl; }

private:
  static A a; //  15 ()
};

A B::a; //  18 ()

int main() {
  B b;
  return 0;
}


Sekarang, setelah kita mendefinisikan anggota kelas statis kita pada baris 18, kita dapat melihat keluaran program berikut:

Pembuat A

Pembuat B

Perusak B

Perusak A


Harus diingat bahwa anggota kelas akan sama untuk semua instance kelas B , mis. jika kita membuat tiga objek kelas B , maka konstruktor dari anggota kelas statis hanya akan dipanggil sekali. Inilah contoh dari apa yang saya bicarakan:



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B {
public:
  B() { std::cout << "Constructor B" << count++ << std::endl; }
  ~B() { std::cout << "Destructor B" << --count << std::endl; }

private:
  static A a; // 
  static int count; // 
};

A B::a; // 
int B::count = 1; // 

int main() {
  B b1, b2, b3;
  return 0;
}


Keluaran program:

Pembuat A

Pembuat B1

Pembuat B2

Pembuat B3

Perusak B3

Perusak B2

Perusak B1

Perusak A


Fungsi statis



Fungsi statis datang ke C ++ dari C. Secara default, semua fungsi di C bersifat global, dan jika Anda ingin membuat dua fungsi dengan nama yang sama dalam dua file .c (.cpp) berbeda dari proyek yang sama, Anda akan mendapatkan pesan kesalahan yang menyatakan bahwa fungsi ini sudah ditentukan ( kesalahan fatal LNK1169: satu atau lebih simbol yang ditentukan banyak ditemukan ). Di bawah ini adalah daftar tiga file dari satu program.



// extend_math.cpp
int sum(int a, int b) {
  int some_coefficient = 1;
  return a + b + some_coefficient;
}


// math.cpp
int sum(int a, int b) {
  return a + b;
}


// main.cpp
 int sum(int, int); // declaration

int main() {
  int result = sum(1, 2);
  return 0;
}


Untuk memperbaiki masalah ini, kami akan mendeklarasikan salah satu fungsi statis. Misalnya yang ini:



// extend_math.cpp
static int sum(int a, int b) {
  int some_coefficient = 1;
  return a + b + some_coefficient;
}


, , . sum() math.cpp . , static , , , , , (.h).



, inline static, , . (.cpp), #include , . , , .. include .cpp .





- ()



Anda dapat menggunakan fungsi anggota statis tanpa membuat objek kelas. Fungsi statis diakses menggunakan nama kelas dan operator resolusi cakupan (: :) . Saat menggunakan fungsi anggota statis, ada batasan seperti:



  1. Dalam suatu fungsi, Anda hanya dapat mengakses anggota data statis, fungsi anggota statis lainnya, dan fungsi lain apa pun dari luar kelas.
  2. Fungsi anggota statis memiliki cakupan kelas tempat mereka berada.
  3. Anda tidak memiliki akses ke penunjuk kelas ini, karena kami tidak membuat objek apa pun untuk memanggil fungsi ini.


Mari kita lihat contoh berikut ini:



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }

  static void foo() { //  8
    std::cout << "static foo()" << std::endl;
  }
};

int main() {
  A::foo(); //  14
  return 0;
}


Di kelas A di baris 8 , kami memiliki fungsi anggota statis foo () . Pada baris 14 , kami memanggil fungsi menggunakan nama kelas dan operator resolusi cakupan, dan kami mendapatkan keluaran program berikut:

foo statis ()


Dari output, Anda dapat melihat bahwa tidak ada pembuatan objek dan tidak ada konstruktor / destruktor yang dipanggil.



Jika metode foo () non-statis, kompilator akan menampilkan kesalahan pada ekspresi di baris 14 , karena Anda perlu membuat objek untuk mengakses metode non-statisnya.





Kesimpulan



« static , ». , , .



:



  • , . , , , . , , .
  • , , .. , . , , .. .
  • static Singleton, , . , -. Singleton , , .
  • Kadang-kadang, agar suatu fungsi hanya berjalan sekali tanpa menyimpan keadaan sebelumnya di suatu tempat di objek, variabel statis digunakan. Anda dapat melihat contoh di bagian "Variabel statis di dalam fungsi". Namun ini bukanlah pendekatan yang sangat baik, dan dapat menyebabkan perburuan bug berjam-jam jika Anda menggunakan multithreading.
  • Dalam praktiknya, programmer C ++ sering menggunakan fungsi anggota statis sebagai alternatif fungsi reguler yang tidak memerlukan objek yang akan dibuat untuk dieksekusi.


Saya harap Anda menikmati artikel saya tentang kata kunci statis di C ++. Saya akan dengan senang hati menerima kritik dan saran. Terimakasih untuk semua!



All Articles