C++ कोर गाइडलाइन्स और प्रोफ़ेशनल पैटर्न
C++ कोर गाइडलाइन्स, नेमिंग कन्वेंशन, const करेक्टनेस, API डिज़ाइन के सिद्धांत, डॉक्यूमेंटेशन प्रैक्टिस, परफ़ॉर्मेंस प्रोफ़ाइलिंग और प्रोडक्शन डीबगिंग तकनीकें सीखें।
प्रोफ़ेशनल C++ कोड लिखना
सही और कुशल C++ लिखना प्रोफ़ेशनल सॉफ़्टवेयर डेवलपमेंट के लिए ज़रूरी तो है, पर काफ़ी नहीं। प्रोफ़ेशनल C++ का मतलब ऐसा कोड भी है जो पठनीय हो (दूसरों और भविष्य में आपको ख़ुद को समझ आए), मेंटेन करने योग्य हो (बग लाए बिना आसानी से बदला जा सके) और मज़बूत हो (एरर को सलीक़े से संभाले और अनुमानित ढंग से फ़ेल हो)। C++ कोर गाइडलाइन्स, जिन्हें Bjarne Stroustrup और Herb Sutter मेंटेन करते हैं, दशकों के कठिन अनुभव से मिली समझ को व्यावहारिक नियमों में समेटती हैं। इस लेसन में सबसे प्रभावशाली गाइडलाइन्स और प्रोफ़ेशनल प्रैक्टिसेज़ शामिल हैं।
C++ कोर गाइडलाइन्स का परिचय
कोर गाइडलाइन्स रिसोर्स मैनेजमेंट, इंटरफ़ेस, एरर हैंडलिंग और नेमिंग को कवर करती हैं। व्यवहार में कुछ सबसे ज़रूरी नियम ये हैं:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <span>
#include <stdexcept>
// R.1: Manage resources automatically using RAII
class DatabaseConnection {
public:
explicit DatabaseConnection(const std::string& connection_string) {
// Acquire resource in constructor
std::cout << "Connected to: " << connection_string << "\n";
}
~DatabaseConnection() {
// Release resource in destructor — always runs
std::cout << "Disconnected\n";
}
// Rule of 5: if you define a destructor, define or delete all 5
DatabaseConnection(const DatabaseConnection&) = delete;
DatabaseConnection& operator=(const DatabaseConnection&) = delete;
DatabaseConnection(DatabaseConnection&&) noexcept = default;
DatabaseConnection& operator=(DatabaseConnection&&) noexcept = default;
void query(std::string_view sql) const {
std::cout << "Executing: " << sql << "\n";
}
};
// I.4: Make interfaces precisely and strongly typed
// BAD: void process(int type, void* data, int size);
// GOOD:
enum class DataType { json, xml, csv };
void process(DataType type, std::span<const std::byte> data) {
// type-safe, no raw pointers, size is carried with the data
}
// F.3: Keep functions short and simple
[[nodiscard]] bool is_valid_email(std::string_view email) {
auto at_pos = email.find('@');
if (at_pos == std::string_view::npos) return false;
auto dot_pos = email.find('.', at_pos);
return dot_pos != std::string_view::npos && dot_pos > at_pos + 1;
}
int main() {
// R.5: Prefer scoped objects — no unnecessary heap allocation
DatabaseConnection db("host=localhost dbname=mydb");
db.query("SELECT * FROM users");
// db is automatically closed when it goes out of scope
// R.11: Avoid raw new/delete — use smart pointers
auto widget = std::make_unique<std::vector<int>>(100);
std::cout << is_valid_email("user@example.com") << "\n"; // 1
std::cout << is_valid_email("invalid-email") << "\n"; // 0
return 0;
}अनुशासन के रूप में Const करेक्टनेस
Const करेक्टनेस केवल आकस्मिक बदलाव रोकने के लिए नहीं है — यह इरादे को दर्ज करती है, कंपाइलर ऑप्टिमाइज़ेशन संभव बनाती है और थ्रेड-सेफ़ी में मदद करती है (जब तक कोई थ्रेड उसी डेटा को बदल नहीं रहा और mutable मेंबर इस्तेमाल नहीं हुए हों, const ऑब्जेक्ट को कई थ्रेड से पढ़ना सुरक्षित है)। जब तक किसी चीज़ को म्यूटेबल होना ज़रूरी न हो, हर चीज़ को const बनाएँ:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
class StudentRegistry {
public:
// const method — promises not to modify the object
[[nodiscard]] size_t count() const { return students_.size(); }
// const reference return — caller can read but not modify
[[nodiscard]] const std::string& get_name(size_t index) const {
if (index >= students_.size())
throw std::out_of_range("Invalid student index");
return students_[index];
}
// Non-const method — modifies the object
void add_student(std::string name) {
students_.push_back(std::move(name));
}
// const-correct algorithm usage
[[nodiscard]] bool has_student(std::string_view name) const {
return std::any_of(students_.cbegin(), students_.cend(),
[name](const std::string& s) { return s == name; });
}
private:
std::vector<std::string> students_;
};
void print_registry(const StudentRegistry& reg) {
// Can only call const methods on a const reference
for (size_t i = 0; i < reg.count(); ++i) {
std::cout << reg.get_name(i) << "\n";
}
// reg.add_student("Eve"); // ERROR: cannot call non-const method
}
int main() {
StudentRegistry reg;
reg.add_student("Alice");
reg.add_student("Bob");
print_registry(reg);
// const local variables
const auto count = reg.count(); // immutable
const auto& name = reg.get_name(0); // immutable reference
std::cout << "Count: " << count << ", First: " << name << "\n";
return 0;
}API डिज़ाइन के सिद्धांत
C++ API डिज़ाइन का सुनहरा नियम, जो Scott Meyers से जुड़ा है: इंटरफ़ेस को सही ढंग से इस्तेमाल करना आसान और ग़लत ढंग से इस्तेमाल करना कठिन बनाएँ।
स्ट्रॉन्ग टाइप का उपयोग करें: void set_timeout(int ms) की जगह void set_timeout(std::chrono::milliseconds timeout) का उपयोग करें। जब आप मिलीसेकंड की उम्मीद करते हैं, तब कॉलर ग़लती से सेकंड नहीं भेज सकता।
[[nodiscard]] का उपयोग करें: जो भी फ़ंक्शन एरर कोड, रिसोर्स या गणना की हुई वैल्यू लौटाता है, उसे [[nodiscard]] होना चाहिए। रिटर्न वैल्यू को अनदेखा करना लगभग हमेशा बग होता है।
वैल्यू सिमेंटिक्स को प्राथमिकता दें: छोटे टाइप को वैल्यू से और बड़े टाइप को const& से पास करें। वैल्यू से रिटर्न करें (कंपाइलर कॉपी एलिज़न लागू करता है)। आउटपुट पैरामीटर से बचें।
पब्लिक सतह को न्यूनतम रखें: हर पब्लिक मेंबर एक प्रतिबद्धता है जिसे आपको निभाना होगा। डिफ़ॉल्ट रूप से private का उपयोग करें और केवल वही उजागर करें जो ज़रूरी है।
कम से कम चौंकाने के सिद्धांत का पालन करें: फ़ंक्शन को वही करना चाहिए जो उसका नाम बताता है, उससे ज़्यादा कुछ नहीं। साइड इफ़ेक्ट नाम से ही स्पष्ट होने चाहिए (save_to_file, process नहीं)।
नेमिंग कन्वेंशन और डॉक्यूमेंटेशन
एकसमान नेमिंग टीम में कोड को पठनीय बनाती है। आम C++ कन्वेंशन:
टाइप: PascalCase — class HttpClient, struct Point3D, enum class Color
फ़ंक्शन/मेथड: snake_case या camelCase — calculate_total(), getSize()
वेरिएबल: snake_case — int item_count, double total_price
मेंबर वेरिएबल: अंत में अंडरस्कोर — int count_, std::string name_
कॉन्स्टेंट: k प्रीफ़िक्स या UPPER_SNAKE — constexpr int kMaxRetries = 3
नेमस्पेस: lowercase — namespace network, namespace detail
एक कन्वेंशन चुनें और उसे पूरे प्रोजेक्ट में लागू करें (clang-format + clang-tidy इसे स्वचालित कर सकते हैं)।
डॉक्यूमेंटेशन: पब्लिक API के लिए Doxygen-शैली के कमेंट उपयोग करें:
/// @brief डेटाबेस से कनेक्ट करता है।
/// @param connection_string डेटाबेस कनेक्शन URI।
/// @return कनेक्शन सफल होने पर true।
/// @throws कनेक्शन विफल होने पर DatabaseError।
[[nodiscard]] bool connect(std::string_view connection_string);परफ़ॉर्मेंस प्रोफ़ाइलिंग
प्रोफ़ाइलिंग किए बिना कभी ऑप्टिमाइज़ न करें। ये टूल बताते हैं कि समय असल में कहाँ ख़र्च हो रहा है, और अंदाज़े की जगह डेटा देते हैं:
# Linux: perf — hardware performance counters
perf record -g ./my_program # record call graph
perf report # interactive flamegraph viewer
# Linux: perf stat — high-level statistics
perf stat ./my_program
# Output: cycles, instructions, cache misses, branch misses, IPC
# macOS: Instruments (via Xcode)
xcrun xctrace record --template 'Time Profiler' --launch ./my_program
# Cross-platform: Google Benchmark (micro-benchmarking)
# In your code:
#include <benchmark/benchmark.h>
static void BM_VectorPushBack(benchmark::State& state) {
for (auto _ : state) {
std::vector<int> v;
for (int i = 0; i < state.range(0); ++i) {
v.push_back(i);
}
benchmark::DoNotOptimize(v.data());
}
}
BENCHMARK(BM_VectorPushBack)->Range(8, 1 << 16);
static void BM_VectorReserved(benchmark::State& state) {
for (auto _ : state) {
std::vector<int> v;
v.reserve(state.range(0));
for (int i = 0; i < state.range(0); ++i) {
v.push_back(i);
}
benchmark::DoNotOptimize(v.data());
}
}
BENCHMARK(BM_VectorReserved)->Range(8, 1 << 16);
BENCHMARK_MAIN();C++ कोड रिव्यू करते समय इन आम समस्याओं को जाँचें:
1. रिसोर्स मैनेजमेंट: क्या हर रिसोर्स (मेमोरी, फ़ाइलें, लॉक) RAII से मैनेज होता है? क्या कहीं रॉ new/delete है?
2. Const करेक्टनेस: क्या पैरामीटर, मेंबर फ़ंक्शन और लोकल वेरिएबल जहाँ संभव हो const हैं?
3. एरर हैंडलिंग: क्या रिटर्न वैल्यू जाँची जाती हैं? क्या एक्सेप्शन उचित स्तरों पर पकड़े जाते हैं? क्या std::expected या एरर कोड का एकसमान उपयोग हुआ है?
4. थ्रेड सेफ़्टी: क्या साझा रिसोर्स सुरक्षित हैं? क्या डेटा रेस संभव हैं? क्या सैनिटाइज़र समस्याएँ पकड़ लेगा?
5. मूव सिमेंटिक्स: जहाँ उचित हो, क्या ऑब्जेक्ट कॉपी होने की जगह मूव होते हैं? क्या const ऑब्जेक्ट पर std::move कॉल हैं (जो चुपचाप कॉपी करते हैं)?
6. API की स्पष्टता: क्या फ़ंक्शन के नाम वर्णनात्मक हैं? क्या पैरामीटर स्ट्रॉन्ग टाइप के हैं? क्या महत्वपूर्ण रिटर्न वैल्यू पर [[nodiscard]] है?
7. बिल्ड चेतावनियाँ: क्या कोड -Wall -Wextra -Wpedantic के साथ साफ़-सुथरा कंपाइल होता है? क्या सभी चेतावनियाँ सुलझाई गई हैं, दबाई नहीं गईं?
8. टेस्टिंग: क्या एज केस टेस्ट होते हैं? क्या नए कोड पाथ टेस्ट से कवर हैं? क्या सैनिटाइज़र पास होते हैं?
प्रोडक्शन में C++ डीबग करना डेवलपमेंट से कठिन है। इसके लिए पहले से तैयारी करें:
हमेशा डीबग सिंबल के साथ शिप करें: प्रोडक्शन बाइनरी के लिए RelWithDebInfo बिल्ड करें। अलग .debug फ़ाइल में डीबग सिंबल रनटाइम पर कोई ओवरहेड नहीं जोड़ते, लेकिन क्रैश का विश्लेषण संभव बनाते हैं।
स्ट्रक्चर्ड डेटा लॉग करें: लॉग लेवल के साथ स्ट्रक्चर्ड लॉगिंग (JSON) का उपयोग करें। टाइमस्टैम्प, थ्रेड ID और रिक्वेस्ट ID शामिल करें। तेज़, टाइप-सेफ़ लॉगिंग के लिए spdlog या std::format का उपयोग करें।
कोर डंप: कोर डंप चालू करें (ulimit -c unlimited) और जानें कि gdb से उनका विश्लेषण कैसे करें। सिंबल सर्वर डिप्लॉय किए गए बाइनरी की डीबग जानकारी रखते हैं।
प्रोडक्शन के आम बग:
- मेमोरी लीक — स्टेजिंग वातावरण में ASan, या Valgrind / Heaptrack जैसे टूल का उपयोग करें
- डेडलॉक — टेस्टिंग में TSan का उपयोग करें, लॉक हासिल करने का क्रम लॉग करें, लॉक टाइमआउट सेट करें
- परफ़ॉर्मेंस रिग्रेशन — CI में लगातार बेंचमार्किंग डिप्लॉयमेंट से पहले रिग्रेशन पकड़ लेती है
- ABI संगतता — स्ट्रक्ट लेआउट या वर्चुअल फ़ंक्शन टेबल बदलने से ABI टूटता है; स्थिर ABI के लिए Pimpl idiom का उपयोग करें
- C++ कोर गाइडलाइन्स का पालन करें — रिसोर्स के लिए RAII, Rule of 5, स्ट्रॉन्ग टाइपिंग, न्यूनतम पब्लिक API
- हर चीज़ को डिफ़ॉल्ट रूप से
constबनाएँ — यह इरादे को दर्ज करता है, बग रोकता है और ऑप्टिमाइज़ेशन संभव बनाता है - ऐसे API डिज़ाइन करें जिन्हें सही ढंग से इस्तेमाल करना आसान और ग़लत ढंग से इस्तेमाल करना कठिन हो — स्ट्रॉन्ग टाइप,
[[nodiscard]], वैल्यू सिमेंटिक्स - ऑप्टिमाइज़ करने से पहले प्रोफ़ाइल करें — असली बॉटलनेक खोजने के लिए
perf, Instruments या Google Benchmark का उपयोग करें - रिसोर्स मैनेजमेंट, const करेक्टनेस, थ्रेड सेफ़्टी और सही एरर हैंडलिंग के लिए कोड रिव्यू करें
- प्रोडक्शन डीबगिंग की तैयारी करें: डीबग सिंबल के साथ शिप करें, स्ट्रक्चर्ड लॉगिंग का उपयोग करें, कोर डंप चालू करें
क्विज़ — अपना ज्ञान परखें
(15 XP)1. C++ कोर गाइडलाइन 'Rule of 5' क्या कहती है?
2. C++ API डिज़ाइन का सुनहरा नियम क्या है?
3. ऑप्टिमाइज़ करने से पहले प्रोफ़ाइल क्यों करना चाहिए?