Güvenlik C ++ 26'da önemli bir konudur ve sözleşmeler muhtemelen en büyük güvenlik özelliğidir. Ancak C ++ 26'nın sunabileceği çok daha fazlası var.
Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.
Bugün C ++ 26'da C ++ 'da tipik güvenlik sorunlarını çözen üç küçük ama önemli iyileştirme yapmak istiyorum.
Geçici bir değişkene döndürülen bir referansın bağlanması
Bir güvenlik sorunu bulmak zor olabilir. Kodun bu bölümü, bu bölümde tekrar tekrar başvurduğum P2748R5 teklifinden geliyor. Minimum ve yürütülebilir bir programa dönüştürdüm.
// bindReferenceToTemporary.cpp
#include <iostream>
#include <string>
#include <string_view>
const std::string_view& getString() {
static std::string s = "Hallo Welt!";
return s;
}
int main() {
std::cout << getString() << 'n';
}
GCC derleyicisi zaten önemli bir hata mesajı yayıyor:

İşte kodda bir hata olan başka bir örnek:
struct X {
const std::map<std::string, int> d_map;
const std::pair<std::string, int>& d_first;
X(const std::map<std::string, int>& map)
: d_map(map), d_first(*d_map.begin()) {}
};
Kodu yazan herkes, anahtar olarak belirtilen çiftin ilk unsurunun sabit olduğu gerçeğini ihmal etti. Bu geçici bir değişken oluşturur. Sonuç olarak, d_first artık geçerli değil.
Bu bizi bir sonraki güvenliğe götürür.
Başlatılmamış okuma süreçleri durumunda yanlış davranış
P2795R5 teklifine atıfta bulunuyorum.
Her şeyden önce, otomatik depolama süresi ve geçici nesnelere sahip nesneler için geçerlidir. Onların tuhaflığı, herhangi bir değerde başlatılmalarıdır. Bu, programın belirsiz bir davranışa sahip olduğu anlamına gelir.
Yani bir soru olmaya devam ediyor: Arşivleme otomatik süresi ne anlama geliyor? Aşağıdaki değişkenlerin otomatik depolama süresi vardır:
- Engelleme alanı olan ve açıkça olmayan değişkenler
static,,thread_localVEYAexternilan edildi. Bu değişkenlerin belleği yalnızca tıkanma tamamlanana kadar geçerli kalır. - Parametrelerin bir alanına ait değişkenler, örneğin bir işlev. Parametrelerin alanı çözüldüğünde otomatik olarak yok edilirler.
Teklifin iki örneği şu terimleri göstermelidir:
extern void f(int);
int main() {
int x; // default-initialized, value of x is indeterminate
f(x); // glvalue-to-prvalue conversion has undefined behaviour
}
void f(T&);
void g(bool);
void h() {
T* p; // uninitialized, has erroneous value (e.g. null)
bool b; // uninitialized, erroneous value may not be valid bool
f(*p); // dereferencing has undefined behaviour
g(b); // undefined behaviour if b is invalid
}
Konuya ulaşmak için: Teklif, C ++ 26'daki yanlış programlarda belirsiz bir davranış olan başlatılmayan okuma süreçlerini C ++ 23'e dönüştürdü.
Tabii ki, otomatik değişkenlerin tam olarak başlatılması nispeten karmaşık bir süreç olabilir. Bu nedenle bir feragat mekanizması vardır:
Öznitelik [[indeterminate]] devre dışı bırakma mekanizmasını temsil eder. Bu işlev sadece uzmanlar için tasarlanmıştır. Öznitelik, program arızalı olmadan başlatılmamış otomatik depolamalı değişkenleri aydınlatmanıza olanak tanır. Aşağıdaki basitleştirilmiş örnek tekliften gelir:
int x [[indeterminate]];
std::cin >> x;
[[indeterminate]] int a, b[10], c[10][10];
compute_values(&a, b, c, 10);
// This class benefits from avoiding determinate-storage initialization guards.
struct SelfStorage {
std::byte data_[512];
void f(); // uses data_ as scratch space
};
SelfStorage s [[indeterminate]]; // documentation suggested this
void g([[indeterminate]] SelfStorage s = SelfStorage()); // same; unusual, but plausible
Son güvenlik işlevi eksik türleri etkiler.
Bir işaretçinin eksik bir türde kabul edilemez bir şekilde ortadan kaldırılması
Eksik bir tür, yalnızca beyanname için bir tür veri türüdür, ancak tanım yoktur. Eksik bir türe işaretçi veya referans mükemmeldir. Bu tür verilerin boyutunu, düzenini veya üye işlevlerini gerektiren işlemler yanlıştır.
Yeni işlev bir şekilde daha spesifiktir: bu tür bir sınıfın önemsiz bir yok edicisi ve sınıfın spesifik bir dizokasyon fonksiyonu olmadığı sürece, bir işaretçinin eksik bir sınıf türüne ortadan kaldırılması doğrudur. Bu, derleyicinin E sınıfının yoksulluğunu oluşturduğu anlamına gelir. operator delete Sınıfta aşırı yüklenmedi.
Teklif, önemsiz bir muhrip ve önemsiz olmayan bir muhrip arasındaki güzel farkı göstermek için iyi bir örnek sunuyor:
Banal yıkıcılar:
// trivialDestructor.cpp
namespace xyz {
struct Widget; // forward decl
Widget *new_widget();
} // namespace xyz
int main() {
xyz::Widget *p = xyz::new_widget();
delete p;
}
namespace xyz {
struct Widget {
const char *d_name;
int d_data;
// (implicit) trivial destructor
// This is the only difference.
};
Widget *new_widget() {
return new Widget();
}
} // namespace xyz
Banal Olmayan Destroyer:
// nontrivialDestructor.cpp
namespace xyz {
struct Widget; // forward decl
Widget *new_widget();
} // namespace xyz
int main() {
xyz::Widget *p = xyz::new_widget();
delete p;
}
namespace xyz {
struct Widget {
const char *d_name;
int d_data;
~Widget() {} // nontrivial dtor
// This is the only difference.
};
Widget *new_widget() {
return new Widget();
}
} // namespace xyz
İkinci örnek, önemsiz olmayan bir muhrip içerir. Teklife göre, derleme bir hata mesajı ile durduruldu:

Sırada ne var?
Bir sonraki makalemde, modelden modelin etrafında bazı küçük iyileştirmelerle ilgileneceğim. Bu gelişmeler esas olarak C ++ 'da tutarsız davranışı ortadan kaldırmaya hizmet eder.
(RME)

Bir yanıt yazın