Rule of Three C++ में एक क्लास डिजाइन दिशानिर्देश है: यदि आपकी क्लास को निम्न में से किसी की स्पष्ट परिभाषा की आवश्यकता है, तो संभवतः तीनों की आवश्यकता होगी:
- कॉपी कंस्ट्रक्टर (
Class(const Class&)) - कॉपी असाइनमेंट ऑपरेटर (
Class& operator=(const Class&)) - डिस्ट्रक्टर (
~Class())
क्यों?
यदि क्लास किसी भी ऊपर वाले को परिभाषित करती है, तो यह डायनामिकली अलोकेटेड रिसोर्सेज (मेमोरी, फाइल हैंडल्स, सॉकेट्स, आदि) को मैनेज कर रही है। डिफॉल्ट (कंपाइलर-जनरेटेड) संस्करण शैलो कॉपी करते हैं जो डबल-फ्री, मेमोरी लीक, या डैंगलिंग पॉइंटर्स का कारण बनते हैं।
समस्या: डिफॉल्ट शैलो कॉपी
class BadString {
char* data;
size_t len;
public:
BadString(const char* s) : len(strlen(s)) {
data = new char[len + 1];
strcpy(data, s);
}
// कोई कॉपी कंस्ट्रक्टर, असाइनमेंट, डिस्ट्रक्टर नहीं!
};
int main() {
BadString s1("Hello");
BadString s2 = s1; // शैलो कॉपी - दोनों समान data पॉइंटर शेयर करते हैं!
// s1 और s2 दोनों नष्ट होंगे → डबल फ्री → क्रैश!
}
समाधान: Rule of Three लागू करना
#include <iostream>
#include <cstring>
using namespace std;
class GoodString {
char* data;
size_t len;
public:
// 1. कंस्ट्रक्टर
GoodString(const char* s = "") : len(strlen(s)) {
data = new char[len + 1];
strcpy(data, s);
cout << "Constructed: " << data << endl;
}
// 2. डिस्ट्रक्टर
~GoodString() {
cout << "Destroyed: " << (data ? data : "null") << endl;
delete[] data;
}
// 3. कॉपी कंस्ट्रक्टर (डीप कॉपी)
GoodString(const GoodString& other) : len(other.len) {
data = new char[len + 1];
strcpy(data, other.data);
cout << "Copy constructed: " << data << endl;
}
// 4. कॉपी असाइनमेंट ऑपरेटर
GoodString& operator=(const GoodString& other) {
cout << "Copy assigned: " << other.data << endl;
if (this != &other) { // सेल्फ-असाइनमेंट चेक
delete[] data; // पुराना रिसोर्स रिलीज
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
return *this;
}
void print() const { cout << data << endl; }
};
int main() {
GoodString s1("Hello");
GoodString s2 = s1; // कॉपी कंस्ट्रक्टर
GoodString s3("World");
s3 = s1; // कॉपी असाइनमेंट
s1.print(); s2.print(); s3.print();
} // तीनों डिस्ट्रक्ट होते हैं - कोई डबल फ्री नहीं!
आउटपुट
Constructed: Hello Copy constructed: Hello Constructed: World Copy assigned: Hello Hello Hello Hello Destroyed: Hello Destroyed: Hello Destroyed: Hello
C++11: Rule of Five (फाइव का नियम)
C++11 में मूव सेमांटिक्स जुड़ने के बाद, यदि आप मूव ऑपरेशन्स को परिभाषित करते हैं, तो पांचों की आवश्यकता होती है:
- डिस्ट्रक्टर
- कॉपी कंस्ट्रक्टर
- कॉपी असाइनमेंट
- मूव कंस्ट्रक्टर (
Class(Class&&)) - मूव असाइनमेंट (
Class& operator=(Class&&))
class ModernString {
char* data;
size_t len;
public:
// ... उपरोक्त सभी ...
// 5. मूव कंस्ट्रक्टर
ModernString(ModernString&& other) noexcept
: data(other.data), len(other.len) {
other.data = nullptr;
other.len = 0;
cout << "Move constructed\n";
}
// 6. मूव असाइनमेंट
ModernString& operator=(ModernString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
len = other.len;
other.data = nullptr;
other.len = 0;
cout << "Move assigned\n";
}
return *this;
}
};
Rule of Zero (शून्य का नियम) - आधुनिक बेस्ट प्रैक्टिस
रॉ पॉइंटर्स के बजाय std::string, std::vector, std::unique_ptr जैसे RAII टाइप्स उपयोग करें। फिर आपको कोई भी स्पेशल मेंबर फंक्शन लिखने की आवश्यकता नहीं - कंपाइलर-जनरेटेड सही काम करते हैं!
#include <string>
#include <vector>
#include <memory>
class BestPractice {
std::string name; // खुद मैनेज करता है
std::vector<int> data; // खुद मैनेज करता है
std::unique_ptr<Resource> res; // खुद मैनेज करता है
// कोई डिस्ट्रक्टर, कॉपी, मूव नहीं चाहिए - Rule of Zero!
};