C++ 'a yeniden hoş geldiniz - Modern C++
C++, kuruluşundan bu yana dünyanın en yaygın kullanılan programlama dillerinden biri haline gelmiştir. İyi yazılmış C++ programları hızlı ve verimlidir. Dil diğer dillerden daha esnektir: En yüksek soyutlama düzeylerinde ve silikon düzeyinde aşağıda çalışabilir. C++ yüksek oranda iyileştirilmiş standart kitaplıklar sağlar. Hızı en üst düzeye çıkarmak ve bellek gereksinimlerini en aza indirmek için alt düzey donanım özelliklerine erişim sağlar. C++ neredeyse her tür program oluşturabilir: Oyunlar, cihaz sürücüleri, HPC, bulut, masaüstü, eklenmiş ve mobil uygulamalar ve çok daha fazlası. Diğer programlama dilleri için kitaplıklar ve derleyiciler bile C++ dilinde yazılır.
C++ için özgün gereksinimlerden biri C diliyle geriye dönük uyumluluktu. Sonuç olarak, C++ her zaman ham işaretçiler, diziler, null ile sonlandırılan karakter dizeleri ve diğer özelliklerle C stili programlamaya izin verir. Bunlar harika bir performans sağlayabilir, ancak hataları ve karmaşıklığı da oluşturabilir. C++'ın evrimi, C stili deyimleri kullanma ihtiyacını büyük ölçüde azaltan özellikleri vurgulamıştır. Eski C programlama tesisleri ihtiyacınız olduğunda hala oradadır. Ancak, modern C++ kodunda bunlara daha az ve daha az ihtiyacınız olmalıdır. Modern C++ kodu daha basit, daha güvenli, daha zarif ve her zamanki gibi hızlıdır.
Aşağıdaki bölümlerde modern C++'ın ana özelliklerine genel bir bakış sağlanmaktadır. Aksi belirtilmediği sürece, burada listelenen özellikler C++11 ve sonraki sürümlerde kullanılabilir. Microsoft C++ derleyicisinde, projeniz için hangi standart sürümünün kullanılacağını belirtmek için derleyici seçeneğini ayarlayabilirsiniz /std
.
Kaynaklar ve akıllı işaretçiler
C stili programlamadaki başlıca hata sınıflarından biri bellek sızıntısıdır. Sızıntılar genellikle ile ayrılan belleğin çağrılmamasından delete
new
kaynaklanmıştır. Modern C++, kaynak edinme ilkesinin başlatma (RAII) olduğunu vurgular. Fikir basit. Kaynaklar (yığın belleği, dosya tanıtıcıları, yuvalar vb.) bir nesneye ait olmalıdır. Bu nesne, oluşturucusunda yeni ayrılan kaynağı oluşturur veya alır ve yok edicisinde siler. RAII ilkesi, sahip olan nesne kapsam dışına çıktığında tüm kaynakların düzgün bir şekilde işletim sistemine döndürüldüğünü garanti eder.
RAII ilkelerinin kolay benimsenmesini desteklemek için C++ Standart Kitaplığı üç akıllı işaretçi türü sağlar: std::unique_ptr
, std::shared_ptr
ve std::weak_ptr
. Akıllı işaretçi, sahip olduğu belleğin ayrılmasını ve silinmesini işler. Aşağıdaki örnekte, çağrısındaki make_unique()
yığında ayrılmış bir dizi üyesine sahip bir sınıf gösterilmektedir. ve delete
çağrıları new
sınıfı tarafından unique_ptr
kapsüllenir. Bir widget
nesne kapsam dışına çıktığında, unique_ptr yıkıcı çağrılır ve dizi için ayrılan belleği serbest bırakır.
#include <memory>
class widget
{
private:
std::unique_ptr<int[]> data;
public:
widget(const int size) { data = std::make_unique<int[]>(size); }
void do_something() {}
};
void functionUsingWidget() {
widget w(1000000); // lifetime automatically tied to enclosing scope
// constructs w, including the w.data gadget member
// ...
w.do_something();
// ...
} // automatic destruction and deallocation for w and w.data
Mümkün olduğunda yığın belleğini yönetmek için akıllı bir işaretçi kullanın. ve delete
işleçlerini açıkça kullanmanız new
gerekiyorsa RAII ilkesini izleyin. Daha fazla bilgi için bkz . Nesne ömrü ve kaynak yönetimi (RAII).
std::string
ve std::string_view
C stili dizeler, hataların bir diğer önemli kaynağıdır. ve std::wstring
kullanarakstd::string
, C stili dizelerle ilişkili neredeyse tüm hataları ortadan kaldırabilirsiniz. Ayrıca, arama, ekleme, ön ödeme vb. için üye işlevlerinin avantajından da yararlanabilirsiniz. Her ikisi de hız için yüksek oranda optimize edilmiştir. Yalnızca salt okunur erişim gerektiren bir işleve dize geçirirken, C++17'de daha da yüksek performans avantajı için kullanabilirsiniz std::string_view
.
std::vector
ve diğer Standart Kitaplık kapsayıcıları
Standart kitaplık kapsayıcılarının tümü RAII ilkesini izler. Öğelerin güvenli geçişi için yineleyiciler sağlar. Ayrıca performans için son derece iyileştirilmiştir ve doğruluk açısından kapsamlı bir şekilde test edilmiştir. Bu kapsayıcıları kullanarak, özel veri yapılarında ortaya çıkabilecek hata veya verimsizlik olasılığını ortadan kaldırırsınız. Ham diziler yerine C++'ta sıralı kapsayıcı olarak kullanın vector
.
vector<string> apples;
apples.push_back("Granny Smith");
Varsayılan ilişkilendirici kapsayıcı olarak (değilunordered_map
) kullanın map
. Degenerate ve multiset
multi servis talepleri için , multimap
ve kullanınset
.
map<string, string> apple_color;
// ...
apple_color["Granny Smith"] = "Green";
Performans iyileştirmesi gerektiğinde şunları kullanmayı göz önünde bulundurun:
- gibi
unordered_map
sıralı olmayan ilişkilendirme kapsayıcıları. Bunlar daha düşük öğe başına ek yüke ve sabit zamanlı aramaya sahiptir, ancak bunların doğru ve verimli bir şekilde kullanılması daha zor olabilir. - Sıralandı
vector
. Daha fazla bilgi için bkz . Algoritmalar.
C stili diziler kullanmayın. Verilere doğrudan erişmesi gereken eski API'ler için bunun yerine gibi f(vec.data(), vec.size());
erişimci yöntemlerini kullanın. Kapsayıcılar hakkında daha fazla bilgi için bkz . C++ Standart Kitaplık Kapsayıcıları.
Standart Kitaplık algoritmaları
Programınız için özel bir algoritma yazmanız gerektiğini varsaymadan önce C++ Standart Kitaplık algoritmalarını gözden geçirin. Standart Kitaplık, arama, sıralama, filtreleme ve rastgele hale getirme gibi birçok yaygın işlem için sürekli büyüyen bir algoritma yelpazesi içerir. Matematik kitaplığı kapsamlıdır. C++17 ve sonraki sürümlerde birçok algoritmanın paralel sürümleri sağlanır.
Aşağıda bazı önemli örnekler verilmiştir:
for_each
, varsayılan dolaşma algoritması (aralık tabanlıfor
döngülerle birlikte).transform
, kapsayıcı öğelerinin yerinde olmayan değişiklikleri içinfind_if
, varsayılan arama algoritmasıdır.sort
,lower_bound
ve diğer varsayılan sıralama ve arama algoritmaları.
Karşılaştırıcı yazmak için katı <
kullanın ve uygun olduğunda adlandırılmış lambdalar kullanın.
auto comp = [](const widget& w1, const widget& w2)
{ return w1.weight() < w2.weight(); }
sort( v.begin(), v.end(), comp );
auto i = lower_bound( v.begin(), v.end(), widget{0}, comp );
auto
açık tür adları yerine
C++11 değişken, işlev ve şablon bildirimlerinde kullanmak üzere anahtar sözcüğünü kullanıma sunms auto
. auto
derleyiciye, açıkça yazmanız gerekmeyecek şekilde nesnenin türünü çıkarması için bildirir. auto
özellikle çıkarılmış tür iç içe yerleştirilmiş bir şablon olduğunda kullanışlıdır:
map<int,list<string>>::iterator i = m.begin(); // C-style
auto i = m.begin(); // modern C++
Aralık tabanlı for
döngüler
Diziler ve kapsayıcılar üzerinde C stili yineleme, dizin oluşturma hatalarına açıktır ve ayrıca yazmak için de yorucudur. Bu hataları ortadan kaldırmak ve kodunuzu daha okunabilir hale getirmek için hem Standart Kitaplık kapsayıcılarıyla hem de ham dizilerle aralık tabanlı for
döngüleri kullanın. Daha fazla bilgi için bkz . Aralık tabanlı for
deyim.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v {1,2,3};
// C-style
for(int i = 0; i < v.size(); ++i)
{
std::cout << v[i];
}
// Modern C++:
for(auto& num : v)
{
std::cout << num;
}
}
constexpr
makrolar yerine ifadeler
C ve C++ içindeki makrolar, derlemeden önce önişlemci tarafından işlenen belirteçlerdir. Bir makro belirtecinin her örneği, dosya derilmeden önce tanımlı değeri veya ifadesiyle değiştirilir. Makrolar, derleme zamanı sabit değerlerini tanımlamak için C stili programlamada yaygın olarak kullanılır. Ancak makrolar hataya açıktır ve hata ayıklaması zordur. Modern C++ dilinde derleme zamanı sabitleri için değişkenleri tercih constexpr
etmelisiniz:
#define SIZE 10 // C-style
constexpr int size = 10; // modern C++
Tekdüzen başlatma
Modern C++ dilinde, herhangi bir tür için ayraç başlatmayı kullanabilirsiniz. Bu başlatma biçimi özellikle diziler, vektörler veya diğer kapsayıcılar başlatılırken kullanışlıdır. Aşağıdaki örnekte, v2
üç örneğiyle S
başlatılır. v3
, küme ayraçları S
kullanılarak başlatılan üç örneğiyle başlatılır. Derleyici, bildirilen türüne göre her öğenin türünü çıkarsar v3
.
#include <vector>
struct S
{
std::string name;
float num;
S(std::string s, float f) : name(s), num(f) {}
};
int main()
{
// C-style initialization
std::vector<S> v;
S s1("Norah", 2.7);
S s2("Frank", 3.5);
S s3("Jeri", 85.9);
v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
// Modern C++:
std::vector<S> v2 {s1, s2, s3};
// or...
std::vector<S> v3{ {"Norah", 2.7}, {"Frank", 3.5}, {"Jeri", 85.9} };
}
Daha fazla bilgi için bkz . Küme ayracı başlatma.
Semantiği taşıma
Modern C++, gereksiz bellek kopyalarını ortadan kaldırmayı mümkün kılan taşıma semantiği sağlar. Dilin önceki sürümlerinde, bazı durumlarda kopyalar kaçınılmazdı. Taşıma işlemi, kopyalama yapmadan bir kaynağın sahipliğini bir nesneden diğerine aktarır. Bazı sınıflar yığın belleği, dosya tanıtıcıları gibi kaynaklara sahip olur. Kaynağa sahip bir sınıf uyguladığınızda, bunun için bir taşıma oluşturucu ve taşıma atama işleci tanımlayabilirsiniz. Derleyici, bir kopyanın gerekli olmadığı durumlarda aşırı yükleme çözümlemesi sırasında bu özel üyeleri seçer. Standart Kitaplık kapsayıcı türleri, nesne tanımlanmışsa nesnelerde taşıma oluşturucuyu çağırır. Daha fazla bilgi için bkz . Oluşturucuları Taşıma ve Atama İşleçlerini Taşıma (C++).
Lambda ifadeleri
C stili programlamada bir işlev, işlev işaretçisi kullanılarak başka bir işleve geçirilebilir. İşlev işaretçilerinin bakımı ve anlaşılması zor. Başvuracakları işlev, kaynak kodun çağrıldığı noktadan çok uzak olan başka bir yerde tanımlanabilir. Ayrıca, bunlar tür açısından güvenli değildir. Modern C++, işlecini geçersiz kılan operator()
ve işlev gibi çağrılmalarını sağlayan işlev nesneleri sağlar. İşlev nesneleri oluşturmanın en kullanışlı yolu satır içi lambda ifadeleridir. Aşağıdaki örnekte, bir işlev nesnesini geçirmek için lambda ifadesinin nasıl kullanılacağı ve işlevin find_if
vektördeki her öğede nasıl çağrılacağı gösterilmektedir:
std::vector<int> v {1,2,3,4,5};
int x = 2;
int y = 4;
auto result = find_if(begin(v), end(v), [=](int i) { return i > x && i < y; });
Lambda ifadesi [=](int i) { return i > x && i < y; }
"türünde int
tek bir bağımsız değişken alan ve bağımsız değişkenin değerinden büyük x
ve küçük y
olup olmadığını belirten bir boole döndüren işlev" olarak okunabilir. Lambda'da ve çevresindeki bağlamdan değişkenlerin x
y
kullanılabildiğini unutmayın. bu [=]
değişkenlerin değere göre yakalandığını belirtir; başka bir deyişle lambda ifadesinin bu değerlerin kendi kopyaları vardır.
Özel durumlar
Modern C++ hata koşullarını bildirmenin ve işlemenin en iyi yolu olarak hata kodlarını değil özel durumları vurgular. Daha fazla bilgi için bkz . Özel durumlar ve hata işleme için modern C++ en iyi yöntemleri.
std::atomic
İş parçacıkları arası iletişim mekanizmaları için C++ Standart Kitaplık std::atomic
yapısını ve ilgili türleri kullanın.
std::variant
(C++17)
Birleşimler, farklı türlerdeki üyelerin aynı bellek konumunu kaplamasına olanak tanıyarak bellek tasarrufu sağlamak için C stili programlamada yaygın olarak kullanılır. Ancak birleşimler tür açısından güvenli değildir ve programlama hatalarına açıktır. C++17, sınıfı birleşimlere std::variant
daha sağlam ve güvenli bir alternatif olarak tanıtır. işlevi, std::visit
türün variant
üyelerine tür güvenli bir şekilde erişmek için kullanılabilir.
Ayrıca bkz.
C++ Dil Başvurusu
Lambda İfadeleri
C++ Standart Kitaplığı
Microsoft C/C++ dil uyumluluğu
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin