Güvenlik
Olabildiği Kadar Çok Const
const
derleyiciye o değişkenin veya metodun değiştirilemez (immutable) olduğunu söyler. Bu derleyicinin kodu optimize etmesine ve developer'ın fonksiyonun beklenenin dışında birşey yapabilecekse farkında olmasına yardımcı olur. Ayrıca const &
kullanmak, derleyicinin gereksiz yere kopyalama yapmasını önler. John Carmack'ın const
üzerine yorumları da iyi bir okumadır.
// Kotu Fikir
class MyClass
{
public:
void do_something(int i);
void do_something(std::string str);
};
// Iyi Fikir
class MyClass
{
public:
void do_something(const int i);
void do_something(const std::string &str);
};
Dönüş Tiplerini Dikkatlice Düşün
- Getter fonksiyonlar
&
veyaconst &
ile döndürmek, döndürülen değerin normal kullanımı gözlem amaçlı olduğunda önemli performans kazanımları sağlar- Eğer döndürülen değer normal kullanımda ne de olsa bir kopya üretecekse, değer ile döndürmek thread güvenliği için daha iyidir, bir performaz kaybı oluşturmaz
- Eğer API'n birbirinin çeşidi (covariant) return tipleri kullanıyorsa,
&
veya*
ile döndürmelisin
- Geçici ve lokal değer
- Daima değer ile döndür
kaynaklar: https://github.com/lefticus/cppbestpractices/issues/21 https://twitter.com/lefticus/status/635943577328095232
Basit tipleri fonksiyonlara const ref ile geçme ve fonksiyonlardan döndürme
// Cok Kotu Bir Fikir
class MyClass
{
public:
explicit MyClass(const int& t_int_value)
: m_int_value(t_int_value)
{
}
const int& get_int_value() const
{
return m_int_value;
}
private:
int m_int_value;
}
Bunun yerine, basit tipleri değer ile al ve döndür. Eğer fonksiyona geçilen değeri değiştimeyeceksen, const
olarak tanımla const
ref değil:
// Iyi Fikir
class MyClass
{
public:
explicit MyClass(const int t_int_value)
: m_int_value(t_int_value)
{
}
int get_int_value() const
{
return m_int_value;
}
private:
int m_int_value;
}
Peki neden? Çünkü referans ile geçmek ve döndürmek pointer operasyonlarını tetikler, değer ile geçmek hızlıca işlemci registar'larını kullanır.
Ham Hafıza Erişimi Yapma
Ham hafıza erişimi, hafızadan alma, o yeri serbest bırakma, hafıza hataları ve akıntısı yapmadan düzgün bir şekilde yapmak C++ dilinde zordur. C++11 bu problemleri önlemek için araçlar sunar.
// Kotu Fikir
MyClass *myobj = new MyClass;
// ...
delete myobj;
// Iyi Fikir
auto myobj = std::make_unique<MyClass>(constructor_param1, constructor_param2); // C++14
auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, constructor_param2)); // C++11
auto mybuffer = std::make_unique<char[]>(length); // C++14
auto mybuffer = std::unique_ptr<char[]>(new char[length]); // C++11
// veya referans sayan nesneler için
auto myobj = std::make_shared<MyClass>();
// ...
// myobj kullanılmadığında otomatik olarak serbest bırakılır.
C-stili Diziler Yerine std::array
veya std::vector
Kullan
Her ikisi de nesnelerin bitişik bellek düzenini garanti eder ve çıplak pointer'ların kullanılmaması için listelenen birçok nedenden ötürü C-stili dizilerin kullanımının yerine geçebilir (ve geçmelidir).
Ayrıca, std::shared_ptr
kullanarak dizi tutmaktan kaçınmalısın.
Exception'ları Kullan
Exception'lar(istisnai durumlar) gözardı edilemez. Dönüş değerleri, boost:optional
gibi, önemsenmeyebilir ve eğer kontrol edilmezse çökmelere ve hafıza hatalarına neden olur. Diğer taraftan bir exception yakalanabilir ve düzeltilebilir. Potansiyel olarak uygulamanın en üst katmanında bir log tutulur ve uygulama otomatik olarak yeniden başlatır.
Stroustrup, C++ dilinin yaratıcısı, bu konuyu benim yapabileceğimden çok daha iyi açıklıyor.
C-stili cast yerine C++-stili cast Kullan
C-stili cast yerine C++-stili cast (static_cast<>, dynamic_cast<> ...) kullan. C++-stili cast derleyicinin daha fazla kontrolüne izin verir ve daha güvenlidir.
// Kotu Fikir
double x = getX();
int i = (int) x;
// Kotu bir fikir degil
int i = static_cast<int>(x);
Ek olarak C++ cast stili daha iyi görülebilir ve arandığında bulması daha kolaydır.
Eğer double
ı int
e cast etmen gerekiyorsa program mantığını refaktör etmeyi düşün (mesela, overflow ve underflow fazladan kontrol etmek gibi). Üç kere ölç ve 0.9999999999981 kere kes.
Variadic fonksiyon tanımlama
Variadic fonksiyonlar değişken sayıda parametre alabilir. Muhtemelen printf() bunun en iyi bilinen örneğidir. Sen de bu türden fonksiyonlar tanımlayabilirsin ama bu olası güvenlik sorunlarına neden olur. Variadic fonksiyonları kullanmak tip güvenli(type safe) değildir ve yanlış parametre geçmek programın belirlenemeyen davranışla sonlandırabilir. Bu beklenmeyen davranış, güvenlik açıklarına neden olabilir. Derleyicin C++11 destekliyorsa, bunun yerine variadic template'leri kullanabilirsin.
Ek Kaynaklar
David Wheeler'ın yazdığı sonraki Heartbleed'i nasıl önleriz kodun mevcut güvenlik durumundan ve güvenliğinden emin olmayı iyi analiz eder.