Theory
एक loop को हर card serve करना है
Closing time। Till आज के swipes पर loop करके discounts compute करता है:
for each Card *c in swipes: total += c->discount();
List में StudentCards और StaffCards mix हैं, और अगले साल GuestCards जुड़ते हैं। Loop को एक line रहना है, फिर भी हर card को अपना rule apply करना है।
दो lessons पहले आपने run-time polymorphism का promise देखा। आज आप उन दो keywords से मिलते हैं जो असल में इसे deliver करते हैं: virtual, और इसका ज़्यादा strict sibling = 0।
Theory
खाली menu board
Head office हर franchise में एक menu board template टाँगता है: slot "आज का discount" इस पर printed है, पर head office value खाली छोड़ता है, हर branch को अपनी भरनी है।
एक virtual function एक board entry है जिसे branches चाहें तो फिर से लिख सकते हैं। एक pure virtual function वह खाली slot है: head office एक ऐसी branch खोलने से मना कर देता है जिसने इसे नहीं भरा। Template ख़ुद एक shop नहीं है जिसमें आप घुस सकें।
Theory
Virtual function, formally
एक virtual function एक member function है जो base class में virtual से declared है:
virtual double discount() { return 0.0; }
इसकी power base-class pointers के साथ दिखती है: Card *c = new StudentCard; इसके बाद c->discount() StudentCard's version run करता है, क्योंकि virtual call को late binding में switch करता है (run time पर असली object से तय)।
virtual के बिना, pointer का अपना type जीतता है और base version run होता है: वह एक keyword ही पूरा फ़र्क़ है।
Theory
Pure virtual, formally
एक body की जगह = 0 लिखिए और function pure virtual बन जाता है:
virtual double discount() = 0;
दो consequences, दोनों exam gold:
- Class एक abstract class बन जाती है: इसके objects बनाना एक compile error है।
- हर derived class को pure virtual function override करना ही होगा, वरना यह ख़ुद abstract रहती है।
एक abstract class एक contract है: यह कहता है "हर card का एक discount rule होगा" बिना यह ढोंग किए कि rule क्या है यह पता है।
Practical
Contract और इसके signers
#include <iostream>
using namespace std;
class Card { // abstract class
public:
virtual double discount() = 0; // pure virtual: the blank slot
virtual ~Card() {} // virtual destructor: good practice
};
class StudentCard : public Card {
public:
double discount() { return 0.10; } // must implement
};
class StaffCard : public Card {
public:
double discount() { return 0.20; } // must implement
};
int main() {
// Card c; // ERROR: cannot create abstract class object
Card *swipes[2] = { new StudentCard, new StaffCard };
for (int i = 0; i < 2; i++)
cout << swipes[i]->discount() << endl; // 0.1 then 0.2
delete swipes[0]; delete swipes[1];
return 0;
}
Think first
दोनों outputs का अंदाज़ा लगाइए
ऊपर वाला code लीजिए और कल्पना कीजिए discount() virtual keyword के BINA है (और = 0 के बिना, इसे 0.0 return करती एक body दीजिए)। तब loop क्या print करता है? और जैसा लिखा है उस code के साथ?
Show the answer
virtual के बिना: pointers Card हैं, तो early binding Card का version दो बार call करता है: यह 0 और 0* print करता है। Children के rules exist करते हैं पर base pointer से कभी पहुँचे नहीं जाते।
जैसा लिखा है (virtual): late binding हर असली object से पूछता है, 0.1 फिर 0.2 print करते हुए।
वही loop, एक keyword का फ़र्क़। अगर एक exam यह pair दिखाकर outputs पूछे, अब आपके पास दोनों हैं।
Quiz
class Card के पास virtual double discount() = 0; है। एक student main() में Card c; लिखता है। Compiler क्या कहता है?
- Error: Card एक abstract class है, इसके objects नहीं बनाए जा सकते
- ठीक है: c.discount() बस 0 return करेगा
- Error: classes में digit 0 नहीं हो सकता
- ठीक है, पर सिर्फ़ तभी अगर StudentCard कहीं exist करता है
Show the answer
Error: Card एक abstract class है, इसके objects नहीं बनाए जा सकते
एक pure virtual function Card को abstract बनाने के लिए काफ़ी है, और abstract classes instantiate नहीं की जा सकतीं: discount() के पीछे run करने के लिए literally कोई code नहीं है। आप फिर भी Card के लिए pointers और references declare कर सकते हैं (यही पूरा मुद्दा है)। Option B = 0 को "0 return करता है" से confuse करता है: इसका मतलब है "कोई body exist नहीं करती", "body zero return करती है" नहीं।
Watch out
तीन precision points
1. = 0 का मतलब है कोई implementation नहीं, "zero return करता है" नहीं।
2. एक derived class जो एक pure virtual function override करना skip करती है वह abstract ही रहती है: कोई sign करे तब तक contract चलता रहता है।
3. Abstract classes के पास data members, normal functions, यहाँ तक कि constructors भी हो सकते हैं; abstract सिर्फ़ direct objects मना करता है। "abstract classes में सिर्फ़ pure virtual functions होते हैं" लिखने पर एक mark कटता है: वह stricter चीज़ एक interface style कहलाती है, कोई requirement नहीं।
Theory
वह pattern जो आप हमेशा reuse करेंगे
Abstract base + concrete children असली software का skeleton है: area() = 0 वाला Shape, calcSalary() = 0 वाला Employee, हर plugin system जो आप कभी मिलेंगे। Interviews में जब vtable mention होता है, यह बस वह lookup table है जो C++ इस run-time routing को काम करने के लिए बनाता है। Mechanism से ज़्यादा concept: हाथ में base pointer, असली object फ़ैसला करता है।
Summary
Key takeaways
- virtual एक base pointer/reference से गुज़रती call को derived override run करवाता है (late binding)।
- virtual के बिना, pointer का type तय करता है और base version run होता है।
- Pure virtual (= 0) की कोई body नहीं होती; ऐसा एक function class को abstract बनाता है।
- Abstract classes instantiate नहीं की जा सकतीं, पर इनके pointers/references ही workhorse हैं।
- Children को हर pure virtual function override करना ही होगा वरना abstract रहते हैं।
- Abstract class = contract: हर card discount() define करेगा।
- Memory hook: वह खाली menu board जिसे हर branch भरे।