C++
आभासी सदस्य कार्य
खोज…
वाक्य - विन्यास
आभासी शून्य च ();
वर्चुअल शून्य जी () = 0;
// C ++ 11 या बाद में:
- आभासी शून्य एच () ओवरराइड;
- शून्य i () ओवरराइड;
- आभासी शून्य () अंतिम;
- शून्य k () अंतिम;
टिप्पणियों
- केवल गैर-स्थिर, गैर-टेम्पलेट सदस्य कार्य
virtual
हो सकते हैं। - यदि आप C ++ 11 या उसके बाद का उपयोग कर रहे हैं, तो बेस क्लास से वर्चुअल सदस्य फ़ंक्शन को
override
करते समयoverride
का उपयोग करने की सिफारिश की जाती है। - पॉलीमॉर्फिक बेस क्लास में अक्सर वर्चुअल डिस्ट्रक्टर्स होते हैं जो किसी ऑब्जेक्ट को बेस क्लास को पॉइंटर के जरिए डिलीट करने की अनुमति देते हैं । यदि विध्वंसक आभासी नहीं थे, तो ऐसा ऑपरेशन अपरिभाषित व्यवहार की ओर जाता है [expr.delete] 35.3.5 / 3 ।
C ++ 11 और बाद में वर्चुअल के साथ ओवरराइड का उपयोग करना
यदि फ़ंक्शन हस्ताक्षर के अंत में निर्दिष्ट किया गया है, तो सीवर 11 में निर्दिष्ट override
का एक विशेष अर्थ है। यह दर्शाता है कि एक फ़ंक्शन है
- बेस क्लास में मौजूद फ़ंक्शन को ओवरराइड करना और
- बेस क्लास फ़ंक्शन
virtual
मुख्य रूप से संकलक के लिए संकेत के रूप में इस विनिर्देश का कोई run time
महत्व नहीं है
नीचे का उदाहरण ओवरराइड का उपयोग किए बिना हमारे साथ व्यवहार में परिवर्तन को प्रदर्शित करेगा।
override
बिना:
#include <iostream>
struct X {
virtual void f() { std::cout << "X::f()\n"; }
};
struct Y : X {
// Y::f() will not override X::f() because it has a different signature,
// but the compiler will accept the code (and silently ignore Y::f()).
virtual void f(int a) { std::cout << a << "\n"; }
};
override
साथ:
#include <iostream>
struct X {
virtual void f() { std::cout << "X::f()\n"; }
};
struct Y : X {
// The compiler will alert you to the fact that Y::f() does not
// actually override anything.
virtual void f(int a) override { std::cout << a << "\n"; }
};
ध्यान दें कि override
एक कीवर्ड नहीं है, लेकिन एक विशेष पहचानकर्ता है जो केवल फ़ंक्शन हस्ताक्षर में दिखाई दे सकता है। अन्य सभी संदर्भों में override
अभी भी एक पहचानकर्ता के रूप में इस्तेमाल किया जा सकता है:
void foo() {
int override = 1; // OK.
int virtual = 2; // Compilation error: keywords can't be used as identifiers.
}
वर्चुअल बनाम गैर-आभासी सदस्य फ़ंक्शन
आभासी सदस्य कार्यों के साथ:
#include <iostream>
struct X {
virtual void f() { std::cout << "X::f()\n"; }
};
struct Y : X {
// Specifying virtual again here is optional
// because it can be inferred from X::f().
virtual void f() { std::cout << "Y::f()\n"; }
};
void call(X& a) {
a.f();
}
int main() {
X x;
Y y;
call(x); // outputs "X::f()"
call(y); // outputs "Y::f()"
}
आभासी सदस्य कार्यों के बिना:
#include <iostream>
struct X {
void f() { std::cout << "X::f()\n"; }
};
struct Y : X {
void f() { std::cout << "Y::f()\n"; }
};
void call(X& a) {
a.f();
}
int main() {
X x;
Y y;
call(x); // outputs "X::f()"
call(y); // outputs "X::f()"
}
अंतिम आभासी कार्य
C ++ 11 ने final
स्पेसिफायर पेश किया जो विधि हस्ताक्षर में प्रकट होने पर विधि को ओवरराइड करने से रोकता है:
class Base {
public:
virtual void foo() {
std::cout << "Base::Foo\n";
}
};
class Derived1 : public Base {
public:
// Overriding Base::foo
void foo() final {
std::cout << "Derived1::Foo\n";
}
};
class Derived2 : public Derived1 {
public:
// Compilation error: cannot override final method
virtual void foo() {
std::cout << "Derived2::Foo\n";
}
};
निर्दिष्ट final
उपयोग केवल 'वर्चुअल' सदस्य फ़ंक्शन के साथ किया जा सकता है और इसे गैर-वर्चुअल सदस्य कार्यों पर लागू नहीं किया जा सकता है
final
तरह, एक निर्दिष्ट कॉलर 'ओवरराइड' भी है जो व्युत्पन्न वर्ग में virtual
कार्यों को ओवरराइड करने से रोकता है।
विनिर्देशक override
और final
वांछित प्रभाव के लिए एक साथ जोड़ सकते हैं:
class Derived1 : public Base {
public:
void foo() final override {
std::cout << "Derived1::Foo\n";
}
};
कंस्ट्रक्टर्स और डिस्ट्रक्टर्स में आभासी कार्यों का व्यवहार
कंस्ट्रक्टर और डिस्ट्रक्टर्स में वर्चुअल फ़ंक्शंस का व्यवहार अक्सर सामना करना पड़ता है जब पहली बार सामना किया जाता है।
#include <iostream>
using namespace std;
class base {
public:
base() { f("base constructor"); }
~base() { f("base destructor"); }
virtual const char* v() { return "base::v()"; }
void f(const char* caller) {
cout << "When called from " << caller << ", " << v() << " gets called.\n";
}
};
class derived : public base {
public:
derived() { f("derived constructor"); }
~derived() { f("derived destructor"); }
const char* v() override { return "derived::v()"; }
};
int main() {
derived d;
}
आउटपुट:
जब बेस कंस्ट्रक्टर से बुलाया जाता है, बेस :: v () कहा जाता है।
व्युत्पन्न कंस्ट्रक्टर से कॉल करने पर, व्युत्पन्न :: v () कहा जाता है।
व्युत्पन्न विध्वंसक से पुकारे जाने पर व्युत्पन्न :: v () कहा जाता है।
जब बेस डिस्ट्रक्टर से कहा जाता है, बेस :: v () कहा जाता है।
इसके पीछे तर्क यह है कि व्युत्पन्न वर्ग अतिरिक्त सदस्यों को परिभाषित कर सकता है जो अभी तक (निर्माण मामले में) या पहले से नष्ट (विध्वंसक मामले में) प्रारंभिक नहीं हैं, और इसके सदस्य कार्यों को कॉल करना असुरक्षित होगा। इसलिए C ++ ऑब्जेक्ट्स के निर्माण और विनाश के दौरान, डायनेमिक प्रकार *this
को कंस्ट्रक्टर या डिस्ट्रक्टर की क्लास माना जाता है, न कि अधिक व्युत्पन्न क्लास।
उदाहरण:
#include <iostream>
#include <memory>
using namespace std;
class base {
public:
base()
{
std::cout << "foo is " << foo() << std::endl;
}
virtual int foo() { return 42; }
};
class derived : public base {
unique_ptr<int> ptr_;
public:
derived(int i) : ptr_(new int(i*i)) { }
// The following cannot be called before derived::derived due to how C++ behaves,
// if it was possible... Kaboom!
int foo() override { return *ptr_; }
};
int main() {
derived d(4);
}
शुद्ध आभासी कार्य
हम यह भी निर्दिष्ट कर सकते हैं कि घोषणा के लिए = 0
जोड़कर एक virtual
फ़ंक्शन शुद्ध आभासी (सार) है। एक या अधिक शुद्ध वर्चुअल फ़ंक्शंस वाली कक्षाएं सार मानी जाती हैं, और इन्हें तत्काल नहीं किया जा सकता; केवल व्युत्पन्न वर्ग जो परिभाषित करते हैं, या के लिए परिभाषाएँ, सभी शुद्ध आभासी कार्यों को तत्काल किया जा सकता है।
struct Abstract {
virtual void f() = 0;
};
struct Concrete {
void f() override {}
};
Abstract a; // Error.
Concrete c; // Good.
भले ही कोई फ़ंक्शन शुद्ध आभासी के रूप में निर्दिष्ट किया गया हो, इसे एक डिफ़ॉल्ट कार्यान्वयन दिया जा सकता है। इसके बावजूद, फ़ंक्शन को अभी भी अमूर्त माना जाएगा, और व्युत्पन्न वर्गों को तत्काल परिभाषित करने से पहले इसे परिभाषित करना होगा। इस स्थिति में, फ़ंक्शन का व्युत्पन्न वर्ग 'संस्करण बेस क्लास' संस्करण को कॉल करने की अनुमति है।
struct DefaultAbstract {
virtual void f() = 0;
};
void DefaultAbstract::f() {}
struct WhyWouldWeDoThis : DefaultAbstract {
void f() override { DefaultAbstract::f(); }
};
हम ऐसा क्यों करना चाहते हैं, इसके कुछ कारण हैं:
अगर हम एक ऐसा वर्ग बनाना चाहते हैं, जो खुद को तुरंत तैयार नहीं कर सकता है, लेकिन इसके व्युत्पन्न वर्गों को तात्कालिक होने से नहीं रोकता है, तो हम विध्वंसक को शुद्ध आभासी घोषित कर सकते हैं। विध्वंसक होने के नाते, इसे वैसे भी परिभाषित किया जाना चाहिए, यदि हम उदाहरण से निपटने में सक्षम होना चाहते हैं। और विध्वंसक के रूप में बहुधा पहले से ही आभासी है पॉलिमॉर्फिक उपयोग के दौरान मेमोरी लीक को रोकने के लिए , हम किसी अन्य फ़ंक्शन को
virtual
घोषित करने से अनावश्यक प्रदर्शन को प्रभावित नहीं करेंगे। इंटरफेस बनाते समय यह उपयोगी हो सकता है।struct Interface { virtual ~Interface() = 0; }; Interface::~Interface() = default; struct Implementation : Interface {}; // ~Implementation() is automatically defined by the compiler if not explicitly // specified, meeting the "must be defined before instantiation" requirement.
यदि शुद्ध वर्चुअल फ़ंक्शन के अधिकांश या सभी कार्यान्वयनों में डुप्लिकेट कोड होगा, तो कोड को आधार वर्ग संस्करण में ले जाया जा सकता है, जिससे कोड को बनाए रखना आसान हो जाता है।
class SharedBase { State my_state; std::unique_ptr<Helper> my_helper; // ... public: virtual void config(const Context& cont) = 0; // ... }; /* virtual */ void SharedBase::config(const Context& cont) { my_helper = new Helper(my_state, cont.relevant_field); do_this(); and_that(); } class OneImplementation : public SharedBase { int i; // ... public: void config(const Context& cont) override; // ... }; void OneImplementation::config(const Context& cont) /* override */ { my_state = { cont.some_field, cont.another_field, i }; SharedBase::config(cont); my_unique_setup(); }; // And so on, for other classes derived from SharedBase.