C++ ile Nesne Yönelimli Programlama
C++ sınıfları, nesne ömrü, referanslar, kaynak yönetimi, aşırı yükleme, kalıtım, çok biçimlilik, akışlar ve standart kütüphane üzerine ayrıntılı ders notları.
C++ notları, aynı dönem tuttuğum C/C++ ders notlarının nesne yönelimli programlama ve kaynak yönetimi tarafını oluşturuyordu. Sonraki yıllarda C++11 sonrasında yerleşen bazı kavramları ekledim; ancak sınıf, nesne ömrü, kalıtım, çok biçimlilik ve standart kütüphane eksenindeki ilk ders yapısını korudum.
Ünite 1: C++'a Geçiş
C ve C++ ilişkisi
C++ birçok C sözdizimi öğesini paylaşır ancak iki dil birebir uyumlu değildir.
Geçerli C kodu her zaman geçerli C++ değildir.
Örnek farklar:
- tür dönüşümü kuralları,
void *dönüşümleri,- karakter sabitlerinin türü,
- anahtar sözcükler,
- designated initialization ayrıntıları,
- değişken uzunluklu diziler,
- standart kütüphane kullanımı.
Bu nedenle .c dosyasını yalnız .cpp olarak yeniden adlandırmak sistematik bir C++ geçiş yöntemi değildir.
C++ programının temel yapısı
#include <iostream>
int main()
{
std::cout << "Merhaba\n";
}C++'ta main dönüş türü yine inttir.
return 0; satırı main sonunda yazılmasa da başarılı sonlandırma kabul edilir, ancak açık yazımı öğretici olabilir.
namespace
Standart C++ kütüphanesinin isimleri:
std::ad alanındadır.
Örnek:
std::cout
std::string
std::vectorGenel başlık dosyalarında:
using namespace std;kullanmak ad çakışmalarını büyütebildiği için önerilmez.
C++ başlatma biçimleri
int a = 10;
int b(10);
int c{10};C++ farklı initialization biçimlerine sahiptir.
Süslü parantezli initialization daraltıcı dönüşümleri engelleyebilir:
int x{3.14}; // hataBu nedenle modern C++ kodunda {} sık kullanılır.
auto
auto count = 10;
auto ratio = 2.5;auto derleyicinin türü initializer'dan çıkarmasını sağlar.
Türü gizlemek amacıyla değil, açıkça çıkarılabildiği veya karmaşık türün tekrarının gereksiz olduğu yerlerde kullanılması uygundur.
const ve constexpr
const nesnenin ilgili erişim yoluyla değiştirilememesini sağlar.
const int limit = 100;constexpr ise sabit ifade bağlamında kullanılabilen değer veya işlevleri tanımlamak için kullanılır:
constexpr int square(int x)
{
return x * x;
}nullptr
C++11 ve sonrasında null pointer için:
nullptrtercih edilir.
int *p = nullptr;0 veya NULL kullanımındaki bazı overload belirsizliklerini ortadan kaldırır.
Giriş ve çıkış
int value{};
std::cin >> value;
std::cout << value << '\n';Metin satırı:
std::string line;
std::getline(std::cin, line);ile okunabilir.
std::string
C++'ta metin için standart sınıf:
std::stringkullanılır.
std::string first = "Ali";
std::string last = "Koker";
std::string full = first + " " + last;C karakter dizileri gerektiğinde kullanılabilir, ancak genel uygulama kodunda std::string bellek yönetimini ve birçok işlemi daha güvenli hale getirir.
std::vector
Dinamik dizi için:
std::vector<int> values;kullanılabilir.
values.push_back(10);
values.push_back(20);Bellek yönetimini otomatik yapar.
Ham:
new int[n]yerine sahiplik gereksinimi yalnız dinamik dizi ise çoğunlukla std::vector daha uygundur.
Ünite 2: Nesneye Yönelik Programlama
Nesne ve sınıf
Sınıf:
- veri,
- bu veri üzerinde çalışan işlevler,
- erişim kuralları,
- nesne yaşam döngüsü
için bir tür tanımıdır.
Örnek:
class Counter {
public:
void increment()
{
++value_;
}
int value() const
{
return value_;
}
private:
int value_{};
};Nesne:
Counter c;sınıfın örneğidir.
Soyutlama
Soyutlama, kullanıcının gerekli davranışı görmesini, gereksiz gerçekleştirim ayrıntılarından bağımsız kalmasını sağlar.
Örneğin:
account.withdraw(100);çağrısının kullanıcı tarafı:
- bakiyenin hangi veri yapısında tutulduğunu,
- işlem kaydının nasıl saklandığını,
- doğrulamanın nasıl yapıldığını
bilmek zorunda değildir.
İyi soyutlama "hangi iş yapılabilir?" sorusunu açık, "nasıl yapılıyor?" ayrıntısını ise gerektiği kadar gizli tutar.
Kapsülleme
Kapsülleme yalnız alanları private yapmak değildir.
Asıl amaç:
- sınıfın geçerli durum koşullarını korumak,
- dış kullanımı kontrollü arabirim üzerinden yapmak,
- iç gerçekleştirimi değiştirebilmektir.
Örnek:
class BankAccount {
public:
explicit BankAccount(double initial)
: balance_{initial}
{
}
bool withdraw(double amount)
{
if (amount < 0 || amount > balance_) {
return false;
}
balance_ -= amount;
return true;
}
double balance() const
{
return balance_;
}
private:
double balance_{};
};Dış kod:
account.balance_ = -1000000;gibi sınıfın kuralını bozan doğrudan değişiklik yapamaz.
public, private ve protected
public üyeler sınıfın dış arabirimidir.
private üyeler sınıf dışından doğrudan kullanılamaz.
protected üyeler sınıf ve türetilmiş sınıflardan erişilebilir.
Veri alanlarını varsayılan olarak private tutmak, davranışı üye işlevlerle yönetmek yaygın tasarım ilkesidir.
struct ve class
C++'ta struct ile class yetenek bakımından büyük ölçüde aynıdır.
Temel fark varsayılan erişimdir:
struct -> public
class -> privateAyrıca struct için varsayılan kalıtım public, class için privatedır.
Basit veri taşıyan türlerde struct, invariant ve davranış içeren kapsüllenmiş türlerde class kullanımı yaygın bir gelenektir.
Kurucu
Constructor nesnenin başlangıç durumunu kurar.
class Point {
public:
Point(int x, int y)
: x_{x}, y_{y}
{
}
private:
int x_;
int y_;
};Üye initializer list:
: x_{x}, y_{y}üyelerin gövdeden önce doğrudan başlatılmasını sağlar.
Varsayılan kurucu
class Point {
public:
Point() = default;
};derleyici tarafından üretilebilen davranışı açıkça talep eder.
explicit
Tek argümanlı kurucularda istenmeyen örtük dönüşümleri engellemek için:
explicit Distance(double meters);kullanılabilir.
Yıkıcı
Destructor:
~Resource()
{
...
}nesnenin yaşamı bittiğinde çalışır.
Modern C++'ta destructor'ın temel önemi RAII ile kaynak serbest bırakmadır.
RAII
Resource Acquisition Is Initialization, kaynağın nesne yaşam süresine bağlanmasıdır.
Örnek kaynaklar:
- dinamik bellek,
- dosya,
- mutex,
- socket,
- işletim sistemi handle'ı.
{
std::ifstream input{"data.txt"};
// ...
}blok sonunda input nesnesinin destructor'ı dosyayı kapatır.
Bu yaklaşım exception dahil bütün çıkış yollarında kaynak yönetimini güvenilir hale getirir.
Bir nesneyi diğerine atama
Point a{1, 2};
Point b{3, 4};
b = a;sınıfın copy assignment işlemini çağırır.
Kullanıcı özel bir kopyalama semantiği tanımlamazsa uygun koşullarda derleyici üye bazlı kopyalama üretir.
Kopya kurucusu
Type(const Type& other);mevcut nesneden yeni nesne oluşturur.
Taşıma semantiği
Modern C++'ta kaynak sahipliğini kopyalamadan aktarabilmek için move semantics bulunur.
Type(Type&& other);
Type& operator=(Type&& other);Bu özellikle:
- dinamik kaynak sahipliği,
- büyük container'lar,
- geçici nesneler
için önemlidir.
Rule of Zero
Sınıf kaynak yönetimini doğrudan ham işaretçilerle yapmak yerine:
std::vector,std::string,std::unique_ptr,- standart kaynak yöneticileri
kullanıyorsa çoğu durumda destructor, copy ve move işlevlerini elle yazmaya gerek kalmaz.
Bu Rule of Zero yaklaşımıdır ve modern C++ için tercih edilir.
Ünite 3: C++ Referansları, Nesne İşaretçileri ve Bellek Yönetimi
Referans
C++ reference:
int x = 10;
int& ref = x;başlangıçta bir nesneye bağlanır.
ref = 20;x değerini değiştirir.
C++ referansı C'deki işaretçi ile aynı dil özelliği değildir.
Değer ile parametre
void f(int x);nesnenin değeri fonksiyon parametresine aktarılır.
const referans
Büyük nesneleri kopyalamadan salt okunur geçirmek için:
void print(const std::string& text);kullanılabilir.
Referans ile değiştirme
void increment(int& value)
{
++value;
}çağıranın nesnesini doğrudan değiştirebilir.
İşaretçi ve referans seçimi
Kabaca:
- null olabilme anlamlıysa işaretçi,
- "bu nesne mutlaka vardır" ilişkisi varsa referans
uygun olabilir.
Ancak sahiplik, yaşam süresi ve API sözleşmesi de seçime dahildir.
this
Statik olmayan üye işlev içinde:
thismevcut nesneyi gösterir.
class Point {
public:
Point& setX(int x)
{
this->x_ = x;
return *this;
}
private:
int x_{};
};Birçok durumda this-> açıkça yazılmak zorunda değildir.
new ve delete
Tek nesne:
int *p = new int{42};
delete p;Dizi:
int *a = new int[100];
delete[] a;Doğru eşleşme gerekir:
new -> delete
new[] -> delete[]Ham new/delete neden sınırlandırılmalı?
Manuel sahiplik:
- leak,
- double delete,
- exception sırasında kaynak kaybı,
- karmaşık yaşam süresi
sorunlarına açıktır.
Modern C++'ta:
std::vector<int>
std::string
std::unique_ptr<T>
std::shared_ptr<T>gibi RAII türleri tercih edilir.
unique_ptr
Tek sahiplik:
auto p = std::make_unique<MyType>();p scope dışına çıktığında nesne otomatik silinir.
Kopyalanamaz, taşınabilir.
shared_ptr
Paylaşılan sahiplik:
auto p = std::make_shared<MyType>();referans sayımı kullanır.
Her nesne ilişkisi için shared_ptr kullanmak doğru değildir. Sahiplik gerçekten paylaşılıyorsa kullanılmalıdır.
Karşılıklı güçlü referanslar döngü oluşturabilir. Gerekli yerde:
std::weak_ptrkullanılır.
Nesne dizileri
std::array<Point, 10> fixed_points;sabit boyut için uygundur.
Dinamik boyut:
std::vector<Point> points;ile daha doğal yönetilir.
Ham:
Point *p = new Point[n];modern genel uygulama kodunda çoğunlukla gerekli değildir.
Ünite 4: Fonksiyon ve İşleç Aşırı Yükleme
Fonksiyon aşırı yükleme
Aynı ad farklı parametre listeleriyle kullanılabilir:
int abs_value(int x);
double abs_value(double x);Derleyici çağrıdaki argüman türlerine göre uygun overload'u seçer.
Yalnız dönüş türünün değişmesi yeterli değildir:
int f(int);
double f(int); // geçersizVarsayılan argüman
void log(std::string_view text, int level = 1);çağrılar:
log("başladı");
log("hata", 3);biçiminde olabilir.
Varsayılan argümanlar parametre listesinin uygun sağ tarafında düzenlenmelidir.
Kurucu aşırı yükleme
class Point {
public:
Point() = default;
Point(int x, int y)
: x_{x}, y_{y}
{
}
private:
int x_{};
int y_{};
};aynı sınıfı farklı başlangıç verileriyle kurmayı sağlar.
Delege eden kurucu
class Point {
public:
Point()
: Point{0, 0}
{
}
Point(int x, int y)
: x_{x}, y_{y}
{
}
private:
int x_;
int y_;
};başlatma mantığının tekrarını azaltır.
İşleç aşırı yükleme
Kullanıcı tanımlı türler için birçok işlecin davranışı tanımlanabilir.
Örnek:
class Vector2 {
public:
Vector2(double x, double y)
: x_{x}, y_{y}
{
}
Vector2 operator+(const Vector2& other) const
{
return {x_ + other.x_, y_ + other.y_};
}
private:
double x_;
double y_;
};Kullanım:
Vector2 c = a + b;İşleç aşırı yüklemede tasarım
İşlecin anlamı kullanıcı beklentisine yakın kalmalıdır.
Örneğin:
a + bifadesinin a nesnesini sürpriz biçimde silmesi veya dosyayı kapatması kötü tasarımdır.
Aşırı yükleme sözdizim kolaylığı sağlar, anlam karmaşasını haklı çıkarmaz.
Karşılaştırma
Modern C++'ta eşitlik ve sıralama işleçleri uygun biçimde tanımlanabilir.
C++20 ile üç yönlü karşılaştırma:
operator<=>bazı türlerde karşılaştırma işlemlerini sadeleştirebilir.
Artırma işleci
Prefix:
T& operator++();Postfix:
T operator++(int);dummy int parametresi iki overload'u ayırır.
Ünite 5: Kalıtım ve Çok Biçimlilik
Kalıtım
Bir sınıf başka bir sınıfın arabirim ve davranışının bir bölümünü devralabilir.
class Shape {
...
};
class Circle : public Shape {
...
};Public kalıtım genel olarak:
Circle bir Shape'dirilişkisini modellemelidir.
Sırf kod tekrarını azaltmak için kalıtım kullanmak uygun olmayabilir. Bileşim çoğu durumda daha esnek çözüm sağlar.
public kalıtım
Temel sınıfın:
publicüyeleri türetilmiş sınıfta public,protectedüyeleri protected
olarak kalır.
private üyelere türetilmiş sınıf doğrudan erişemez.
protected kalıtım
Public ve protected üyeler türetilmiş sınıfta protected hale gelir.
Dış API'de "is-a" ilişkisini sakladığı için genel modellemede daha seyrek kullanılır.
private kalıtım
Temel sınıfın public ve protected üyeleri türetilmiş sınıfta private olur.
Bu daha çok gerçekleştirim tekniği olarak görülebilir. Çoğu durumda composition daha açık olabilir.
Kurucu sırası
Türetilmiş nesne oluşturulurken genel sıra:
temel sınıf
↓
üye nesneler
↓
türetilmiş sınıfYıkım ters sırada olur.
Çoklu kalıtım
C++ birden fazla temel sınıftan kalıtımı destekler:
class C : public A, public B {
};Bu olanak güçlüdür fakat:
- ad belirsizliği,
- diamond problemi,
- karmaşık yaşam döngüsü
oluşturabilir.
Arayüz benzeri küçük soyut temel sınıfların birleştirilmesi daha kontrollü kullanım alanıdır.
Diamond problemi
A
/ \
B C
\ /
DB ve C ayrı ayrı A'dan türetilirse D içinde iki ayrı A alt nesnesi bulunabilir.
Virtual inheritance:
class B : virtual public A {};
class C : virtual public A {};tek ortak A alt nesnesi kullanılmasını sağlayabilir.
Sanal fonksiyon
Çalışma zamanı çok biçimliliği için:
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};Türetilmiş sınıf:
class Circle : public Shape {
public:
explicit Circle(double r)
: r_{r}
{
}
double area() const override
{
return 3.141592653589793 * r_ * r_;
}
private:
double r_;
};override
double area() const override;derleyicinin gerçekten bir temel sınıf sanal işlevinin override edildiğini doğrulamasını sağlar.
Yanlış imza gibi hataları erken yakalar.
virtual destructor
Polimorfik temel sınıf nesnesi temel sınıf işaretçisi üzerinden silinecekse destructor sanal olmalıdır:
virtual ~Base() = default;Aksi halde davranış hatalı olabilir.
Saf sanal fonksiyon
virtual void draw() = 0;sınıfı soyut hale getirir.
Soyut sınıftan doğrudan nesne oluşturulamaz.
Dinamik dispatch
Shape& s = circle;
s.draw();çağrısı nesnenin gerçek dinamik türüne göre uygun override'ı çalıştırır.
Bu runtime polymorphismdir.
Statik çok biçimlilik
C++'ta templates ve overload'lar derleme zamanı çok biçimliliği sağlar.
Örnek:
template <typename T>
T max_value(T a, T b)
{
return a > b ? a : b;
}Bu yapı sanal çağrıdan farklıdır. Uygun kod tür başına derleme sırasında oluşturulur.
Ünite 6: C++ Giriş Çıkış ve Dosyalar
Akımlar
C++ I/O sistemi stream soyutlamasına dayanır.
Temel akımlar:
std::cin
std::cout
std::cerr
std::clogÇıktı
std::cout << "Değer: " << value << '\n';'\n' yalnız yeni satır ekler.
std::endlyeni satırın yanında stream'i flush eder. Her satırda gereksiz std::endl kullanmak performansı azaltabilir.
Girdi
int value{};
if (std::cin >> value) {
...
}Stream durumunu denetlemek hatalı girdiyi yönetmek için önemlidir.
Biçimlendirme
#include <iomanip>
std::cout
<< std::fixed
<< std::setprecision(2)
<< value;Dosya yazma
#include <fstream>
std::ofstream out{"data.txt"};
if (!out) {
// hata
}
out << 10 << ' ' << 20 << '\n';Dosya nesnesi RAII sayesinde kapsam sonunda kapanır.
Dosya okuma
std::ifstream in{"data.txt"};
int a{};
int b{};
if (in >> a >> b) {
...
}Satır okuma
std::string line;
while (std::getline(in, line)) {
...
}İkili dosyalar
std::ofstream out{
"data.bin",
std::ios::binary
};Ham nesne belleğini doğrudan kalıcı biçim olarak yazmak C++ için de taşınabilir değildir.
Özellikle sınıflar için:
- vtable işaretçileri,
- padding,
- işaretçiler,
- endian,
- ABI
nedeniyle açık serileştirme gerekir.
Rastgele erişim
stream.seekg(position);
stream.tellg();
stream.seekp(position);
stream.tellp();ile dosya konumu yönetilebilir.
Ünite 7: C++ Standart Kütüphanesi
Standart Şablon Kütüphanesi yaklaşımı
C++ standart kütüphanesinde container, iterator ve algoritmalar birlikte çalışır.
Kavramsal model:
Container
↓ iterator range
AlgorithmBu ayrım veri yapısı ile algoritmanın gevşek bağlı kalmasını sağlar.
vector
std::vector<int> values{1, 2, 3};özellikleri:
- ardışık bellek,
- rastgele erişim
O(1), - sona ekleme amortize
O(1), - ortada ekleme/silme
O(n).
values.push_back(4);
values.pop_back();std::vector, genel amaçlı değişken boyutlu ardışık veri için çoğu zaman varsayılan container seçimidir.
array
Sabit boyutlu dizi:
std::array<int, 4> values{1, 2, 3, 4};C dizisine benzer ardışık saklama sağlar fakat:
size,- iterator,
- assignment
gibi standart container olanaklarını sunar.
list
std::list<int>çift yönlü bağlantılı listedir.
Bilinen konumda ekleme ve silme sabit zamanlı olabilir.
Ancak:
- rastgele erişim yoktur,
- her elemanda bağlantı maliyeti vardır,
- cache locality düşüktür.
Bu nedenle std::list, std::vector yerine otomatik olarak daha verimli değildir.
deque
std::deque<int>iki uçtan büyüyebilen sıra yapısıdır.
Başta ve sonda ekleme/silme verimlidir.
Bellek yerleşimi vector gibi tek parça ardışık olmak zorunda değildir.
stack
std::stack<int> stack;LIFO adaptörüdür.
stack.push(10);
stack.top();
stack.pop();queue
std::queue<int> queue;FIFO adaptörüdür.
priority_queue
std::priority_queue<int>varsayılan olarak en büyük elemanı tepeye getiren öncelik kuyruğudur.
Heap tabanlı gerçekleştirim yaygındır.
associative containers
std::set
std::map
std::multiset
std::multimapsıralı ilişkisel container'lardır.
Yaygın gerçekleştirim dengeli ağaçtır.
Tipik arama:
O(log n)unordered containers
std::unordered_set
std::unordered_maphash tablosu yaklaşımına dayanır.
Ortalama arama:
O(1)olabilir.
Kötü durum garantisi aynı değildir.
Iterator
Iterator container elemanları üzerinde konum ve dolaşım soyutlamasıdır.
Örnek:
for (auto it = values.begin(); it != values.end(); ++it) {
std::cout << *it << '\n';
}Modern C++'ta range-for daha sade olabilir:
for (const auto& value : values) {
std::cout << value << '\n';
}Iterator kategorileri
Klasik kategoriler:
- input,
- output,
- forward,
- bidirectional,
- random access,
- contiguous
olarak düşünülür.
Her algoritma her iterator türüyle çalışamaz.
Örneğin rastgele erişim gerektiren algoritma std::list iterator'ıyla kullanılamaz.
find
auto it = std::find(values.begin(), values.end(), target);dizilim içinde doğrusal arama yapar.
Bulunamazsa:
it == values.end()olur.
sort
std::sort(values.begin(), values.end());random-access iterator gerektirir.
Standart karmaşıklık:
O(n log n)karşılaştırma düzeyindedir.
count
auto n = std::count(
values.begin(),
values.end(),
target
);eşleşen eleman sayısını verir.
search
Alt dizi araması:
std::search(
sequence.begin(),
sequence.end(),
pattern.begin(),
pattern.end()
);ile yapılabilir.
transform
std::transform(
input.begin(),
input.end(),
output.begin(),
operation
);her elemana dönüşüm uygular.
Modern C++'ta lambda ile:
std::transform(
values.begin(),
values.end(),
result.begin(),
[](int x) { return x * x; }
);kullanımı doğaldır.
Lambda
auto square = [](int x) {
return x * x;
};adı olmayan fonksiyon nesnesi oluşturur.
Algoritmalarla kullanımı:
std::sort(
people.begin(),
people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
}
);ranges
C++20 ve sonrasında ranges kütüphanesi algoritma ve görünüm işlemlerini geliştirir.
Örneğin:
std::ranges::sort(values);iterator çiftini açıkça yazma ihtiyacını azaltabilir.
Views:
- filtreleme,
- dönüştürme,
- tembel veri işleme
için kullanılabilir.
Dersin klasik STL container-iterator-algorithm modeli ranges yaklaşımının da temelini oluşturur.
Ünite 8: Modern C++ Kaynak ve Tür Güvenliği
Raw pointer sahiplik değildir
Ham işaretçi:
T*tek başına nesnenin sahibinin kim olduğunu belirtmez.
Modern tasarımda ayrım açık olmalıdır:
- sahiplik,
- gözlem,
- geçici erişim.
Tek sahiplik:
std::unique_ptr<T>Paylaşılan sahiplik:
std::shared_ptr<T>Sahip olmayan erişim:
T*
T&
std::span<T>gibi türlerle ifade edilebilir.
std::span
Ardışık veri aralığını sahiplenmeden temsil eder:
void process(std::span<const int> values)
{
for (int x : values) {
...
}
}Bu sayede dizi ve std::vector gibi farklı ardışık kaynaklar ortak arabirimle kullanılabilir.
std::string_view
Sahip olmayan metin görünümü:
void print(std::string_view text);gereksiz std::string kopyalarını azaltabilir.
Ancak gösterdiği karakterlerin yaşam süresini uzatmaz. Dangling string_view oluşturulmamalıdır.
Exception
C++ hata aktarımı için exception mekanizması sağlar:
throw std::runtime_error{"hata"};yakalama:
try {
...
} catch (const std::exception& e) {
...
}RAII, exception kullanılsa da kaynakların doğru serbest bırakılabilmesini sağlar.
noexcept
Bir fonksiyon:
void swap(Type& other) noexcept;ile exception fırlatmayacağını bildirebilir.
Özellikle move operation'larda noexcept, standart container optimizasyonlarını etkileyebilir.
Undefined behavior
Hem C hem C++'ta bazı hatalar "hata kodu üretir" biçiminde tanımlanmaz. Davranış tamamen tanımsız olabilir.
Örnekler:
- dizi sınırı dışı erişim,
- serbest bırakılmış belleğe erişim,
- null işaretçiyi dereference etme,
- signed integer overflow,
- bazı yaşam süresi ihlalleri.
Undefined behavior, yalnız beklenmedik çıktı değil, derleyicinin program hakkında çok farklı optimizasyon varsayımları yapabilmesi anlamına gelir.
Derleyici uyarıları
Geliştirme sırasında yüksek uyarı düzeyi kullanılmalıdır.
GCC/Clang için örnek:
-Wall
-Wextra
-Wpedantic
-WconversionHer uyarı mutlak hata değildir, fakat sessiz tür dönüşümleri ve olası kusurları görünür hale getirir.
Sanitizer
Geliştirme ve testte:
AddressSanitizer
UndefinedBehaviorSanitizer
ThreadSanitizergibi araçlar:
- bellek taşması,
- use-after-free,
- undefined behavior,
- veri yarışı
gibi hataları bulmada çok değerlidir.
C ve C++ için güvenli varsayımlar
C'de:
- boyutları açık taşı,
getskullanma,scanfve dosya işlemlerinin dönüşlerini denetle,- dinamik belleğin sahipliğini açık tut,
mallocçarpımında taşmayı değerlendir,- işaretçi yaşam süresini takip et.
C++'ta bunlara ek olarak:
- raw
new/deleteyerine RAII, - C karakter dizisi yerine uygun olduğunda
std::string, - dinamik dizi yerine
std::vector, - null için
nullptr, - sahiplik için smart pointer,
- kopya/move davranışında Rule of Zero,
- polimorfik temelde virtual destructor
tercih edilmelidir.
C ve C++ Arasındaki Temel Farklar
| Konu | C | C++ | | --- | --- | --- | | Temel yaklaşım | Yordamsal ve sistem programlama | Çok paradigmalı | | Sınıf | Yok | Var | | Kalıtım | Yok | Var | | Sanal fonksiyon | Yok | Var | | Fonksiyon aşırı yükleme | Yok | Var | | İşleç aşırı yükleme | Yok | Var | | Reference türü | Yok | Var | | Template | Yok | Var | | Exception | Yok | Var | | Namespace | Yok | Var | | Dinamik bellek | malloc/free | RAII, container, smart pointer, new/delete | | Metin | char dizisi | std::string ve C dizileri | | Dinamik dizi | Manuel bellek | std::vector | | Jenerik programlama | Makro / _Generic gibi olanaklar | Template ve concepts | | Standart algoritmalar | Daha sınırlı | Geniş algorithms/ranges kütüphanesi |
Bu tablo dillerden birinin diğerinden genel olarak "daha iyi" olduğu anlamına gelmez.
C:
- ABI sadeliği,
- küçük runtime,
- gömülü sistemler,
- işletim sistemi çekirdeği,
- donanım arabirimi,
- taşınabilir C API
için güçlüdür.
C++:
- büyük performans duyarlı uygulamalar,
- kaynak güvenliği,
- yüksek düzey soyutlama,
- generic programming,
- oyun motorları,
- gerçek zamanlı sistemler,
- masaüstü uygulamaları,
- yüksek performanslı kütüphaneler
için geniş araçlar sunar.