C++
क्लासेस / संरचनाएं
खोज…
वाक्य - विन्यास
- variable.member_var = स्थिर;
- variable.member_function ();
- variable_pointer-> सदस्य_वर = स्थिर;
- variable_pointer-> member_function ();
टिप्पणियों
ध्यान दें कि struct
और class
कीवर्ड के बीच एकमात्र अंतर यह है कि डिफ़ॉल्ट रूप से, एक struct
के सदस्य चर, सदस्य फ़ंक्शन और आधार वर्ग public
होते हैं, जबकि एक class
वे private
होते हैं। सी ++ प्रोग्रामर इसे क्लास कहते हैं यदि इसमें कंस्ट्रक्टर और डिस्ट्रक्टर्स हैं, और अपने स्वयं के आक्रमणकारियों को लागू करने की क्षमता है; या एक संरचना अगर यह सिर्फ मूल्यों का एक सरल संग्रह है, लेकिन C ++ भाषा ही कोई अंतर नहीं बनाती है।
कक्षा की मूल बातें
एक वर्ग एक उपयोगकर्ता-परिभाषित प्रकार है। class
, struct
या union
कीवर्ड के साथ एक क्लास शुरू की जाती है। बोलचाल के उपयोग में, शब्द "वर्ग" आमतौर पर केवल गैर-संघी वर्गों के लिए होता है।
एक वर्ग कक्षा के सदस्यों का एक संग्रह है, जो हो सकता है:
- सदस्य चर ("फ़ील्ड" भी कहा जाता है),
- सदस्य कार्य (जिन्हें "विधियाँ" भी कहा जाता है),
- सदस्य प्रकार या टाईपडेफ़्स (उदाहरण के लिए "नेस्टेड क्लासेस"),
- सदस्य टेम्पलेट (किसी भी प्रकार: चर, फ़ंक्शन, वर्ग या अन्य नाम टेम्पलेट)
class
और struct
कीवर्ड्स, जिन्हें क्लास कीज कहा जाता है, काफी हद तक इंटरचेंजेबल होते हैं, सिवाय इसके कि सदस्यों और ठिकानों के लिए डिफॉल्ट एक्सेस स्पेसियर class
कुंजी के साथ घोषित "प्राइवेट" है और struct
या union
साथ घोषित क्लास के लिए "पब्लिक" है। (cf. प्रवेश संशोधक )।
उदाहरण के लिए, निम्न कोड स्निपेट समान हैं:
struct Vector
{
int x;
int y;
int z;
};
// are equivalent to
class Vector
{
public:
int x;
int y;
int z;
};
एक क्लास 'घोषित करके आपके प्रोग्राम में एक नया प्रकार जोड़ा जाता है, और इसके द्वारा उस क्लास की वस्तुओं को इंस्टेंट करना संभव है
Vector my_vector;
एक वर्ग के सदस्यों को डॉट-सिंटैक्स का उपयोग करके एक्सेस किया जाता है।
my_vector.x = 10;
my_vector.y = my_vector.x + 1; // my_vector.y = 11;
my_vector.z = my_vector.y - 4; // my:vector.z = 7;
पहुँच निर्दिष्टकर्ता
तीन कीवर्ड हैं जो एक्सेस स्पेसियर्स के रूप में कार्य करते हैं। ये तब तक वर्ग के सदस्यों तक पहुंच को सीमित करते हैं, जब तक कि कोई अन्य विशेष पहुंच स्तर को फिर से नहीं बदलता है:
कीवर्ड | विवरण |
---|---|
public | सभी की पहुंच है |
protected | केवल कक्षा ही, व्युत्पन्न वर्ग और दोस्तों की पहुँच है |
private | केवल वर्ग खुद और दोस्तों के लिए उपयोग किया है |
जब class
कीवर्ड का उपयोग करके प्रकार को परिभाषित किया जाता है, तो डिफ़ॉल्ट एक्सेस स्पेसियर private
, लेकिन यदि struct
कीवर्ड का उपयोग करके टाइप को परिभाषित किया जाता है, तो डिफ़ॉल्ट एक्सेस स्पेसियर public
:
struct MyStruct { int x; };
class MyClass { int x; };
MyStruct s;
s.x = 9; // well formed, because x is public
MyClass c;
c.x = 9; // ill-formed, because x is private
एक्सेस स्पेसर्स का उपयोग ज्यादातर आंतरिक क्षेत्रों और विधियों तक पहुंच को सीमित करने के लिए किया जाता है, और प्रोग्रामर को एक विशिष्ट इंटरफ़ेस का उपयोग करने के लिए मजबूर किया जाता है, उदाहरण के लिए सीधे एक चर को संदर्भित करने के बजाय गेटर्स और सेटर्स के उपयोग को मजबूर करने के लिए:
class MyClass {
public: /* Methods: */
int x() const noexcept { return m_x; }
void setX(int const x) noexcept { m_x = x; }
private: /* Fields: */
int m_x;
};
protected
का उपयोग करना प्रकार की निश्चित कार्यक्षमता की अनुमति देने के लिए उपयोगी है केवल व्युत्पन्न वर्गों के लिए सुलभ है, उदाहरण के लिए, निम्न कोड में, विधि Plus2Base
calculateValue()
केवल बेस क्लास Plus2Base
बेस से प्राप्त होने वाली कक्षाओं के लिए सुलभ है, जैसे कि FortyTwo
:
struct Plus2Base {
int value() noexcept { return calculateValue() + 2; }
protected: /* Methods: */
virtual int calculateValue() noexcept = 0;
};
struct FortyTwo: Plus2Base {
protected: /* Methods: */
int calculateValue() noexcept final override { return 40; }
};
ध्यान दें कि friend
कीवर्ड का उपयोग संरक्षित या निजी सदस्यों तक पहुंचने के लिए कार्यों या प्रकारों तक पहुंच अपवादों को जोड़ने के लिए किया जा सकता है।
public
, protected
और private
कीवर्ड का उपयोग बेस क्लास सबोबिज को एक्सेस देने या सीमित करने के लिए भी किया जा सकता है। इन्हेरिटेंस उदाहरण देखें।
विरासत
वर्गों / संरचनाओं में वंशानुक्रम संबंध हो सकते हैं।
यदि एक वर्ग / struct B
एक वर्ग / struct से विरासत में मिली A
, इस का मतलब है कि B
एक अभिभावक के रूप है A
। हम कहते हैं कि B
एक व्युत्पन्न वर्ग / से struct है A
, और A
आधार वर्ग / struct है।
struct A
{
public:
int p1;
protected:
int p2;
private:
int p3;
};
//Make B inherit publicly (default) from A
struct B : A
{
};
एक वर्ग / संरचना के लिए विरासत के 3 रूप हैं:
-
public
-
private
-
protected
ध्यान दें कि डिफ़ॉल्ट विरासत के सदस्यों की डिफ़ॉल्ट दृश्यता रूप में ही है: public
अगर आप का उपयोग struct
कीवर्ड और private
के लिए class
कीवर्ड।
एक struct
(या इसके विपरीत) से एक class
प्राप्त करना भी संभव है। इस मामले में, डिफ़ॉल्ट उत्तराधिकार बच्चे द्वारा नियंत्रित किया जाता है, इसलिए एक struct
जो एक class
से निकलती है वह सार्वजनिक विरासत में डिफ़ॉल्ट होगी, और एक class
जो एक struct
से struct
होता है, डिफ़ॉल्ट रूप से निजी विरासत होगा।
public
विरासत:
struct B : public A // or just `struct B : A`
{
void foo()
{
p1 = 0; //well formed, p1 is public in B
p2 = 0; //well formed, p2 is protected in B
p3 = 0; //ill formed, p3 is private in A
}
};
B b;
b.p1 = 1; //well formed, p1 is public
b.p2 = 1; //ill formed, p2 is protected
b.p3 = 1; //ill formed, p3 is inaccessible
private
विरासत:
struct B : private A
{
void foo()
{
p1 = 0; //well formed, p1 is private in B
p2 = 0; //well formed, p2 is private in B
p3 = 0; //ill formed, p3 is private in A
}
};
B b;
b.p1 = 1; //ill formed, p1 is private
b.p2 = 1; //ill formed, p2 is private
b.p3 = 1; //ill formed, p3 is inaccessible
protected
विरासत:
struct B : protected A
{
void foo()
{
p1 = 0; //well formed, p1 is protected in B
p2 = 0; //well formed, p2 is protected in B
p3 = 0; //ill formed, p3 is private in A
}
};
B b;
b.p1 = 1; //ill formed, p1 is protected
b.p2 = 1; //ill formed, p2 is protected
b.p3 = 1; //ill formed, p3 is inaccessible
ध्यान दें कि यद्यपि protected
वंशानुक्रम की अनुमति है, लेकिन इसका वास्तविक उपयोग दुर्लभ है। आवेदन में किस तरह से protected
विरासत का उपयोग किया जाता है इसका एक उदाहरण आंशिक आधार वर्ग विशेषज्ञता (आमतौर पर "नियंत्रित बहुरूपता") के रूप में जाना जाता है।
जब ओओपी अपेक्षाकृत नया था, (सार्वजनिक) उत्तराधिकार को अक्सर "आईएस-ए" संबंध बनाने के लिए कहा जाता था। अर्थात्, सार्वजनिक विरासत केवल तभी सही है जब व्युत्पन्न वर्ग का एक उदाहरण भी आधार वर्ग का एक उदाहरण है।
इसे बाद में लिसकोव प्रतिस्थापन सिद्धांत में परिष्कृत किया गया: सार्वजनिक वंशानुक्रम का उपयोग केवल तभी किया जाना चाहिए जब / यदि व्युत्पन्न वर्ग का एक उदाहरण किसी भी संभावित परिस्थिति में आधार वर्ग के उदाहरण के लिए प्रतिस्थापित किया जा सकता है (और अभी भी समझ में आता है)।
निजी विरासत को आम तौर पर एक पूरी तरह से अलग रिश्ते को मूर्त रूप देने के लिए कहा जाता है: "के संदर्भ में लागू किया जाता है" (कभी-कभी इसे "एचएएस-ए" संबंध कहा जाता है)। उदाहरण के लिए, एक Stack
वर्ग एक Vector
वर्ग से निजी तौर पर विरासत में मिला सकता है। निजी विरासत सार्वजनिक विरासत की तुलना में एकत्रीकरण के लिए बहुत अधिक समानता रखती है।
संरक्षित विरासत का उपयोग लगभग कभी नहीं किया जाता है, और यह किस प्रकार के संबंध का प्रतीक है, इस पर कोई सामान्य सहमति नहीं है।
वर्चुअल इनहेरिटेंस
वंशानुक्रम का उपयोग करते समय, आप virtual
कीवर्ड निर्दिष्ट कर सकते हैं:
struct A{};
struct B: public virtual A{};
जब क्लास B
में आभासी आधार A
होता है, तो इसका मतलब है कि A
, वंशानुक्रम के पेड़ के सबसे व्युत्पन्न वर्ग में निवास करेगा , और इस तरह उस आभासी आधार को शुरू करने के लिए सबसे व्युत्पन्न वर्ग भी जिम्मेदार है:
struct A
{
int member;
A(int param)
{
member = param;
}
};
struct B: virtual A
{
B(): A(5){}
};
struct C: B
{
C(): /*A(88)*/ {}
};
void f()
{
C object; //error since C is not initializing it's indirect virtual base `A`
}
अगर हम un-comment /*A(88)*/
तो हमें कोई त्रुटि नहीं मिलेगी क्योंकि C
अब इसे अप्रत्यक्ष रूप से वर्चुअल बेस A
।
यह भी ध्यान रखें कि जब हम चर बना रहे हैं object
, सबसे व्युत्पन्न वर्ग है C
है, तो C
बनाने (के निर्माता बुला) के लिए जिम्मेदार है A
की है और इस तरह मूल्य A::member
है 88
, नहीं 5
(यदि हम थे के रूप में यह हो सकता है प्रकार B
)।
यह हीरे की समस्या को हल करते समय उपयोगी है । ”
A A A
/ \ | |
B C B C
\ / \ /
D D
virtual inheritance normal inheritance
B
और C
दोनों को A
से विरासत में मिली है, और D
को B
और C
से विरासत में मिली C
, इसलिए D
में A
2 उदाहरण हैं! इसका परिणाम अस्पष्टता के रूप में होता है जब आप A
सदस्य को D
माध्यम से एक्सेस कर रहे होते हैं, क्योंकि कंपाइलर को यह जानने का कोई तरीका नहीं होता है कि आप किस वर्ग से उस सदस्य (जिसको B
विरासत में मिला है, या वह जिसे C
से विरासत में मिला है) तक पहुँचना चाहते हैं? ।
आभासी विरासत को हल करती है इस समस्या: आभासी आधार केवल सबसे व्युत्पन्न वस्तु में रहता है के बाद से, उसमें केवल एक ही हो जाएगा A
में D
।
struct A
{
void foo() {}
};
struct B : public /*virtual*/ A {};
struct C : public /*virtual*/ A {};
struct D : public B, public C
{
void bar()
{
foo(); //Error, which foo? B::foo() or C::foo()? - Ambiguous
}
};
टिप्पणियों को हटाने से अस्पष्टता का समाधान होता है।
एकाधिक वंशानुक्रम
एकल वंशानुक्रम के अलावा:
class A {};
class B : public A {};
आपके पास कई उत्तराधिकार भी हो सकते हैं:
class A {};
class B {};
class C : public A, public B {};
C
को अब A
और B
से एक ही समय में विरासत में मिला होगा।
नोट: यह अस्पष्टता को जन्म दे सकता है यदि एक ही नाम कई विरासत वाले class
एस या struct
एस में उपयोग किया जाता है। सावधान रहे!
एकाधिक वंशानुक्रम में अस्पष्टता
मल्टीपल इनहेरिटेंस कुछ मामलों में मददगार हो सकती है, लेकिन कभी-कभी कई तरह की प्रॉब्लम का सामना करना पड़ता है।
उदाहरण के लिए: दो आधार वर्गों के एक ही नाम के साथ कार्य होते हैं जो व्युत्पन्न वर्ग में ओवरराइड नहीं होते हैं और यदि आप व्युत्पन्न वर्ग की वस्तु का उपयोग करके उस फ़ंक्शन तक पहुंचने के लिए कोड लिखते हैं, तो कंपाइलर त्रुटि दिखाता है क्योंकि, यह निर्धारित नहीं कर सकता है कि किस फ़ंक्शन को कॉल करना है। इस प्रकार की विरासत में कई प्रकार की अस्पष्टता के लिए एक कोड है।
class base1
{
public:
void funtion( )
{ //code for base1 function }
};
class base2
{
void function( )
{ // code for base2 function }
};
class derived : public base1, public base2
{
};
int main()
{
derived obj;
// Error because compiler can't figure out which function to call
//either function( ) of base1 or base2 .
obj.function( )
}
लेकिन, इस समस्या को हल किया जा सकता है गुंजाइश रिज़ॉल्यूशन फ़ंक्शन का उपयोग करके यह निर्दिष्ट करने के लिए कि कौन से फ़ंक्शन को या तो base1 या base2 में क्लास करें:
int main()
{
obj.base1::function( ); // Function of class base1 is called.
obj.base2::function( ); // Function of class base2 is called.
}
कक्षा सदस्यों तक पहुँचना
पहुँच सदस्य चर और एक वर्ग का एक उद्देश्य, के सदस्य कार्यों के लिए .
ऑपरेटर का उपयोग किया जाता है:
struct SomeStruct {
int a;
int b;
void foo() {}
};
SomeStruct var;
// Accessing member variable a in var.
std::cout << var.a << std::endl;
// Assigning member variable b in var.
var.b = 1;
// Calling a member function.
var.foo();
जब एक पॉइंटर के माध्यम से एक वर्ग के सदस्यों तक पहुंच, ->
ऑपरेटर आमतौर पर उपयोग किया जाता है। वैकल्पिक रूप से, उदाहरण के लिए अनुमेय और किया जा सकता है .
ऑपरेटर का उपयोग किया जाता है, हालांकि यह कम आम है:
struct SomeStruct {
int a;
int b;
void foo() {}
};
SomeStruct var;
SomeStruct *p = &var;
// Accessing member variable a in var via pointer.
std::cout << p->a << std::endl;
std::cout << (*p).a << std::endl;
// Assigning member variable b in var via pointer.
p->b = 1;
(*p).b = 1;
// Calling a member function via a pointer.
p->foo();
(*p).foo();
स्थिर वर्ग के सदस्यों तक पहुँचने पर, ::
ऑपरेटर का उपयोग किया जाता है, लेकिन इसके उदाहरण के बजाय कक्षा के नाम पर। वैकल्पिक रूप से, स्थैतिक सदस्य को इंस्टेंस या पॉइंटर से एक्सेस करके इंस्टेंस तक पहुँचा जा सकता है .
या ->
ऑपरेटर, गैर-स्थिर सदस्यों तक पहुँचने के समान सिंटैक्स के साथ क्रमशः।
struct SomeStruct {
int a;
int b;
void foo() {}
static int c;
static void bar() {}
};
int SomeStruct::c;
SomeStruct var;
SomeStruct* p = &var;
// Assigning static member variable c in struct SomeStruct.
SomeStruct::c = 5;
// Accessing static member variable c in struct SomeStruct, through var and p.
var.a = var.c;
var.b = p->c;
// Calling a static member function.
SomeStruct::bar();
var.bar();
p->bar();
पृष्ठभूमि
->
ऑपरेटर की जरूरत है क्योंकि सदस्य एक्सेस ऑपरेटर है .
dereferencing ऑपरेटर *
पर पूर्वता है।
किसी को उम्मीद होगी कि *pa
dereference p
(ऑब्जेक्ट p
संदर्भ में इंगित होता है) और उसके बाद उसके सदस्य को एक्सेस करना a
। लेकिन वास्तव में, यह सदस्य का उपयोग करने की कोशिश करता है a
की p
और फिर इसे भिन्नता। यानी *pa
बराबर है *(pa)
। उपरोक्त उदाहरण में, यह एक संकलक त्रुटि में परिणाम होगा की वजह से दोनों तथ्यों: सबसे पहले, p
एक सूचक है और एक सदस्य नहीं है a
। दूसरा, a
पूर्णांक है और इस प्रकार, इसका कोई संदर्भ नहीं दिया जा सकता है।
इस समस्या का असामान्य रूप से उपयोग किया जाने वाला समाधान पूर्वता को स्पष्ट रूप से नियंत्रित करना होगा: (*p).a
इसके बजाय, ->
ऑपरेटर लगभग हमेशा उपयोग किया जाता है। यह पॉइंटर को पहले डीफ्रेंसिंग करने और फिर उस तक पहुंचने के लिए एक छोटा हाथ है। Ie (*p).a
बिल्कुल p->a
के समान p->a
।
::
ऑपरेटर एक स्कोप ऑपरेटर होता है, जिसका उपयोग किसी नेमस्पेस के सदस्य तक पहुँचने के लिए किया जाता है। ऐसा इसलिए है क्योंकि एक स्थिर वर्ग के सदस्य को उस वर्ग के दायरे में माना जाता है, लेकिन उस वर्ग के उदाहरणों का सदस्य नहीं माना जाता है। सामान्य का उपयोग .
और ->
स्थैतिक सदस्यों के लिए भी अनुमति दी जाती है, बावजूद इसके कि वे ऐतिहासिक कारणों से सदस्य नहीं हैं; यह टेम्प्लेट में जेनेरिक कोड लिखने के लिए उपयोग होता है, क्योंकि कॉल करने वाले को इस बात से चिंतित होने की आवश्यकता नहीं है कि दिया गया सदस्य फ़ंक्शन स्थिर है या गैर-स्थिर है।
निजी विरासत: बेस क्लास इंटरफ़ेस को प्रतिबंधित करना
निजी विरासत तब उपयोगी होती है जब उसे वर्ग के सार्वजनिक इंटरफ़ेस को प्रतिबंधित करना आवश्यक होता है:
class A {
public:
int move();
int turn();
};
class B : private A {
public:
using A::turn;
};
B b;
b.move(); // compile error
b.turn(); // OK
यह दृष्टिकोण कुशलता से ए पॉइंटर या संदर्भ के लिए कास्टिंग करके ए सार्वजनिक तरीकों तक पहुंच को रोकता है:
B b;
A& a = static_cast<A&>(b); // compile error
सार्वजनिक विरासत के मामले में, इस तरह की कास्टिंग, व्युत्पन्न बी में इसे रोकने के लिए वैकल्पिक तरीकों के बावजूद सभी ए सार्वजनिक तरीकों तक पहुंच प्रदान करेगी, जैसे कि निवास:
class B : public A {
private:
int move();
};
या निजी का उपयोग कर:
class B : public A {
private:
using A::move;
};
फिर दोनों मामलों के लिए यह संभव है:
B b;
A& a = static_cast<A&>(b); // OK for public inheritance
a.move(); // OK
अंतिम कक्षाएं और संरचनाएं
final
स्पेसियर के साथ एक वर्ग को व्युत्पन्न करना निषिद्ध हो सकता है। एक अंतिम वर्ग घोषित करते हैं:
class A final {
};
अब किसी भी उपवर्ग का प्रयास यह एक संकलन त्रुटि का कारण होगा:
// Compilation error: cannot derive from final class:
class B : public A {
};
अंतिम श्रेणी वर्ग पदानुक्रम में कहीं भी दिखाई दे सकती है:
class A {
};
// OK.
class B final : public A {
};
// Compilation error: cannot derive from final class B.
class C : public B {
};
मित्रता
friend
कीवर्ड का उपयोग अन्य वर्गों और कार्यों को कक्षा के निजी और संरक्षित सदस्यों तक पहुंच प्रदान करने के लिए किया जाता है, यहां तक कि इसके माध्यम से उन्हें वर्ग के दायरे से बाहर भी परिभाषित किया जाता है।
class Animal{
private:
double weight;
double height;
public:
friend void printWeight(Animal animal);
friend class AnimalPrinter;
// A common use for a friend function is to overload the operator<< for streaming.
friend std::ostream& operator<<(std::ostream& os, Animal animal);
};
void printWeight(Animal animal)
{
std::cout << animal.weight << "\n";
}
class AnimalPrinter
{
public:
void print(const Animal& animal)
{
// Because of the `friend class AnimalPrinter;" declaration, we are
// allowed to access private members here.
std::cout << animal.weight << ", " << animal.height << std::endl;
}
}
std::ostream& operator<<(std::ostream& os, Animal animal)
{
os << "Animal height: " << animal.height << "\n";
return os;
}
int main() {
Animal animal = {10, 5};
printWeight(animal);
AnimalPrinter aPrinter;
aPrinter.print(animal);
std::cout << animal;
}
10
10, 5
Animal height: 5
नेस्टेड क्लासेस / स्ट्रक्चर्स
एक class
या struct
अपने अंदर एक अन्य class
/ struct
परिभाषा भी हो सकती है, जिसे "नेस्टेड क्लास" कहा जाता है; इस स्थिति में, युक्त वर्ग को "संलग्न वर्ग" कहा जाता है। नेस्टेड क्लास की परिभाषा को एनक्लोजिंग क्लास का सदस्य माना जाता है, लेकिन अन्यथा अलग है।
struct Outer {
struct Inner { };
};
एनक्लोजिंग क्लास के बाहर से, नेस्टेड क्लासेस को स्कोप ऑपरेटर के उपयोग से एक्सेस किया जाता है। एनक्लोजिंग क्लास के अंदर से, हालांकि, नेस्टेड क्लास को क्वालीफायर के बिना इस्तेमाल किया जा सकता है:
struct Outer {
struct Inner { };
Inner in;
};
// ...
Outer o;
Outer::Inner i = o.in;
एक गैर-नेस्टेड class
/ struct
, सदस्य फ़ंक्शन और स्थिर चर को या तो नेस्टेड वर्ग के भीतर, या एनक्लोजिंग नेमस्पेस में परिभाषित किया जा सकता है। हालाँकि, उन्हें एनक्लोजिंग क्लास के भीतर परिभाषित नहीं किया जा सकता है, क्योंकि इसे नेस्टेड क्लास की तुलना में एक अलग वर्ग माना जाता है।
// Bad.
struct Outer {
struct Inner {
void do_something();
};
void Inner::do_something() {}
};
// Good.
struct Outer {
struct Inner {
void do_something();
};
};
void Outer::Inner::do_something() {}
गैर-नेस्टेड वर्गों के साथ के रूप में, नेस्टेड कक्षाओं को आगे घोषित किया जा सकता है और बाद में परिभाषित किया जा सकता है, बशर्ते कि उन्हें सीधे उपयोग किए जाने से पहले परिभाषित किया जाए।
class Outer {
class Inner1;
class Inner2;
class Inner1 {};
Inner1 in1;
Inner2* in2p;
public:
Outer();
~Outer();
};
class Outer::Inner2 {};
Outer::Outer() : in1(Inner1()), in2p(new Inner2) {}
Outer::~Outer() {
if (in2p) { delete in2p; }
}
C ++ 11 से पहले, नेस्टेड कक्षाओं में केवल नाम, static
सदस्य और संलग्नक वर्ग से एन्यूमरेटर तक पहुंच थी; एनक्लोजिंग क्लास में परिभाषित अन्य सभी सदस्य ऑफ-लिमिट थे।
C ++ 11 के अनुसार, नेस्टेड क्लासेस और उसके सदस्यों के साथ ऐसा व्यवहार किया जाता है जैसे कि वे एनक्लोजिंग क्लास के friend
थे, और सामान्य पहुँच नियमों के अनुसार, अपने सभी सदस्यों तक पहुँच सकते हैं; यदि नेस्टेड क्लास के सदस्यों को एनक्लोजिंग क्लास के एक या अधिक गैर-स्थैतिक सदस्यों का मूल्यांकन करने की क्षमता की आवश्यकता होती है, तो उन्हें एक उदाहरण दिया जाना चाहिए:
class Outer {
struct Inner {
int get_sizeof_x() {
return sizeof(x); // Legal (C++11): x is unevaluated, so no instance is required.
}
int get_x() {
return x; // Illegal: Can't access non-static member without an instance.
}
int get_x(Outer& o) {
return o.x; // Legal (C++11): As a member of Outer, Inner can access private members.
}
};
int x;
};
इसके विपरीत, संलग्न वर्ग को नेस्टेड क्लास के मित्र के रूप में नहीं माना जाता है, और इस प्रकार स्पष्ट रूप से अनुमति दिए बिना अपने निजी सदस्यों तक नहीं पहुंच सकता है।
class Outer {
class Inner {
// friend class Outer;
int x;
};
Inner in;
public:
int get_x() {
return in.x; // Error: int Outer::Inner::x is private.
// Uncomment "friend" line above to fix.
}
};
एक नेस्टेड क्लास के दोस्तों को स्वचालित रूप से एनक्लोजिंग क्लास के दोस्त नहीं माना जाता है; यदि उन्हें संलग्न वर्ग के दोस्त होने की आवश्यकता है, तो इसे अलग से घोषित किया जाना चाहिए। इसके विपरीत, चूंकि संलग्न वर्ग को स्वचालित रूप से नेस्टेड क्लास का दोस्त नहीं माना जाता है, न ही एनक्लोजिंग क्लास के दोस्तों को नेस्टेड क्लास के दोस्त माना जाता है।
class Outer {
friend void barge_out(Outer& out, Inner& in);
class Inner {
friend void barge_in(Outer& out, Inner& in);
int i;
};
int o;
};
void barge_in(Outer& out, Outer::Inner& in) {
int i = in.i; // Good.
int o = out.o; // Error: int Outer::o is private.
}
void barge_out(Outer& out, Outer::Inner& in) {
int i = in.i; // Error: int Outer::Inner::i is private.
int o = out.o; // Good.
}
अन्य सभी वर्ग के सदस्यों की तरह, नेस्टेड कक्षाओं को केवल कक्षा के बाहर से ही नाम दिया जा सकता है, यदि उनके पास सार्वजनिक पहुंच हो। हालाँकि, आपको एक्सेस संशोधक की परवाह किए बिना उन्हें एक्सेस करने की अनुमति है, जब तक आप स्पष्ट रूप से उनका नाम नहीं लेते हैं।
class Outer {
struct Inner {
void func() { std::cout << "I have no private taboo.\n"; }
};
public:
static Inner make_Inner() { return Inner(); }
};
// ...
Outer::Inner oi; // Error: Outer::Inner is private.
auto oi = Outer::make_Inner(); // Good.
oi.func(); // Good.
Outer::make_Inner().func(); // Good.
आप नेस्टेड क्लास के लिए एक प्रकार का उपनाम भी बना सकते हैं। यदि एक प्रकार का उपनाम अन्य संलग्न वर्ग में निहित है, तो नेस्टेड प्रकार और प्रकार का उपनाम अलग-अलग पहुंच संशोधक हो सकते हैं। यदि प्रकार उपनाम अन्य संलग्न वर्ग के बाहर है, तो इसके लिए यह आवश्यक है कि या तो नेस्टेड वर्ग, या एक typedef
चट्टान, सार्वजनिक हो।
class Outer {
class Inner_ {};
public:
typedef Inner_ Inner;
};
typedef Outer::Inner ImOut; // Good.
typedef Outer::Inner_ ImBad; // Error.
// ...
Outer::Inner oi; // Good.
Outer::Inner_ oi; // Error.
ImOut oi; // Good.
अन्य वर्गों के साथ, नेस्टेड कक्षाएं अन्य कक्षाओं से प्राप्त या प्राप्त की जा सकती हैं।
struct Base {};
struct Outer {
struct Inner : Base {};
};
struct Derived : Outer::Inner {};
यह उन परिस्थितियों में उपयोगी हो सकता है जहां प्रोग्रामर को दूसरी कक्षा से प्राप्त किया जाता है, प्रोग्रामर को नेस्टेड क्लास को आवश्यकतानुसार अपडेट करने की अनुमति देता है। इसे प्रत्येक एनक्लोजिंग क्लास 'नेस्टेड क्लास' के लिए एक सुसंगत नाम प्रदान करने के लिए एक टाइफेड के साथ जोड़ा जा सकता है:
class BaseOuter {
struct BaseInner_ {
virtual void do_something() {}
virtual void do_something_else();
} b_in;
public:
typedef BaseInner_ Inner;
virtual ~BaseOuter() = default;
virtual Inner& getInner() { return b_in; }
};
void BaseOuter::BaseInner_::do_something_else() {}
// ---
class DerivedOuter : public BaseOuter {
// Note the use of the qualified typedef; BaseOuter::BaseInner_ is private.
struct DerivedInner_ : BaseOuter::Inner {
void do_something() override {}
void do_something_else() override;
} d_in;
public:
typedef DerivedInner_ Inner;
BaseOuter::Inner& getInner() override { return d_in; }
};
void DerivedOuter::DerivedInner_::do_something_else() {}
// ...
// Calls BaseOuter::BaseInner_::do_something();
BaseOuter* b = new BaseOuter;
BaseOuter::Inner& bin = b->getInner();
bin.do_something();
b->getInner().do_something();
// Calls DerivedOuter::DerivedInner_::do_something();
BaseOuter* d = new DerivedOuter;
BaseOuter::Inner& din = d->getInner();
din.do_something();
d->getInner().do_something();
उपरोक्त मामले में, BaseOuter
और DerivedOuter
दोनों सदस्य प्रकार को क्रमशः BaseInner_
और DerivedInner_
रूप में सदस्य Inner
आपूर्ति करते हैं। यह नेस्टेड प्रकारों को एन्क्लोज़िंग क्लास के इंटरफ़ेस को तोड़ने के बिना प्राप्त करने की अनुमति देता है, और नेस्टेड प्रकार को पॉलीमॉर्फिक रूप से उपयोग करने की अनुमति देता है।
सदस्य प्रकार और उपनाम
एक class
या struct
सदस्य प्रकार के उपनामों को भी परिभाषित कर सकती है, जो प्रकार के उपनाम होते हैं, और वर्ग के सदस्यों के रूप में माना जाता है।
struct IHaveATypedef {
typedef int MyTypedef;
};
struct IHaveATemplateTypedef {
template<typename T>
using MyTemplateTypedef = std::vector<T>;
};
स्थैतिक सदस्यों की तरह, इन टाइपफेड को गुंजाइश ऑपरेटर, ::
का उपयोग करके एक्सेस किया जाता है।
IHaveATypedef::MyTypedef i = 5; // i is an int.
IHaveATemplateTypedef::MyTemplateTypedef<int> v; // v is a std::vector<int>.
सामान्य प्रकार के उपनामों के साथ, प्रत्येक सदस्य प्रकार के उपनाम को किसी भी प्रकार से परिभाषित या पहले नामांकित करने की अनुमति है, लेकिन इसकी परिभाषा के बाद नहीं। इसी तरह, क्लास डेफिनेशन के बाहर एक टाइप्डिफ, क्लास की परिभाषा के भीतर किसी भी सुलभ टाइपफेड को संदर्भित कर सकता है, बशर्ते वह क्लास की परिभाषा के बाद आता है।
template<typename T>
struct Helper {
T get() const { return static_cast<T>(42); }
};
struct IHaveTypedefs {
// typedef MyTypedef NonLinearTypedef; // Error if uncommented.
typedef int MyTypedef;
typedef Helper<MyTypedef> MyTypedefHelper;
};
IHaveTypedefs::MyTypedef i; // x_i is an int.
IHaveTypedefs::MyTypedefHelper hi; // x_hi is a Helper<int>.
typedef IHaveTypedefs::MyTypedef TypedefBeFree;
TypedefBeFree ii; // ii is an int.
सदस्य प्रकार के उपनाम किसी भी पहुंच स्तर के साथ घोषित किए जा सकते हैं, और उपयुक्त एक्सेस संशोधक का सम्मान करेंगे।
class TypedefAccessLevels {
typedef int PrvInt;
protected:
typedef int ProInt;
public:
typedef int PubInt;
};
TypedefAccessLevels::PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.
TypedefAccessLevels::ProInt pro_i; // Error: TypedefAccessLevels::ProInt is protected.
TypedefAccessLevels::PubInt pub_i; // Good.
class Derived : public TypedefAccessLevels {
PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.
ProInt pro_i; // Good.
PubInt pub_i; // Good.
};
इसका उपयोग एब्सट्रैक्शन के स्तर को प्रदान करने के लिए किया जा सकता है, एक वर्ग के डिजाइनर को बिना कोड को तोड़ने वाले अपने आंतरिक कामकाज को बदलने की अनुमति देता है।
class Something {
friend class SomeComplexType;
short s;
// ...
public:
typedef SomeComplexType MyHelper;
MyHelper get_helper() const { return MyHelper(8, s, 19.5, "shoe", false); }
// ...
};
// ...
Something s;
Something::MyHelper hlp = s.get_helper();
इस स्थिति में, यदि हेल्पर क्लास को SomeComplexType
से किसी अन्य प्रकार में बदल दिया SomeComplexType
है, तो केवल typedef
और friend
घोषणा को संशोधित करने की आवश्यकता होगी; जब तक सहायक वर्ग एक ही कार्यक्षमता प्रदान करता है, कोई भी कोड जो इसे Something::MyHelper
रूप में उपयोग करता है Something::MyHelper
नाम से निर्दिष्ट करने के बजाय आमतौर पर अभी भी बिना किसी संशोधन के काम करेगा। इस तरीके से, हम उस कोड की मात्रा को कम कर देते हैं, जिसे अंतर्निहित कार्यान्वयन में परिवर्तित होने पर संशोधित करने की आवश्यकता होती है, जैसे कि केवल एक स्थान पर टाइप नाम को बदलना होगा।
यह भी साथ जोड़ा जा सकता decltype
, अगर एक इच्छाओं को तो।
class SomethingElse {
AnotherComplexType<bool, int, SomeThirdClass> helper;
public:
typedef decltype(helper) MyHelper;
private:
InternalVariable<MyHelper> ivh;
// ...
public:
MyHelper& get_helper() const { return helper; }
// ...
};
इस स्थिति में, decltype
के कार्यान्वयन में परिवर्तन SomethingElse::helper
कारण, स्वचालित रूप से हमारे लिए decltype
बदल जाएगा। जब हम helper
बदलना चाहते हैं, तो यह आवश्यक संशोधनों की संख्या को कम कर देता है, जो मानव त्रुटि के जोखिम को कम करता है।
हालांकि, सब कुछ के साथ, यह बहुत दूर ले जाया जा सकता है। यदि टाइपनेम केवल एक या दो बार आंतरिक और शून्य बार बाहरी रूप से उपयोग किया जाता है, उदाहरण के लिए, इसके लिए एक उपनाम प्रदान करने की कोई आवश्यकता नहीं है। यदि किसी परियोजना के दौरान इसका उपयोग सैकड़ों या हजारों बार किया जाता है, या यदि इसका एक लंबा पर्याप्त नाम है, तो इसे हमेशा पूर्ण शब्दों में उपयोग करने के बजाय इसे एक टाइफाइड के रूप में प्रदान करना उपयोगी हो सकता है। अनावश्यक शोर की मात्रा के साथ फॉरवर्ड संगतता और सुविधा को संतुलित करना चाहिए।
इसका उपयोग टेम्पलेट कक्षाओं के साथ भी किया जा सकता है, कक्षा के बाहर से टेम्पलेट मापदंडों तक पहुंच प्रदान करने के लिए।
template<typename T>
class SomeClass {
// ...
public:
typedef T MyParam;
MyParam getParam() { return static_cast<T>(42); }
};
template<typename T>
typename T::MyParam some_func(T& t) {
return t.getParam();
}
SomeClass<int> si;
int i = some_func(si);
यह आमतौर पर कंटेनरों के साथ उपयोग किया जाता है, जो आमतौर पर सदस्य प्रकार के उपनाम के रूप में उनके तत्व प्रकार, और अन्य सहायक प्रकार प्रदान करेंगे। C ++ मानक पुस्तकालय में अधिकांश कंटेनर, उदाहरण के लिए, निम्नलिखित 12 सहायक प्रकार प्रदान करते हैं, साथ ही किसी अन्य विशेष प्रकार के साथ जिनकी उन्हें आवश्यकता हो सकती है।
template<typename T>
class SomeContainer {
// ...
public:
// Let's provide the same helper types as most standard containers.
typedef T value_type;
typedef std::allocator<value_type> allocator_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef MyIterator<value_type> iterator;
typedef MyConstIterator<value_type> const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
};
C ++ 11 से पहले, यह आमतौर पर "टेम्पलेट typedef
" प्रदान करने के लिए उपयोग किया जाता था, क्योंकि यह सुविधा अभी तक उपलब्ध नहीं थी; ये उपनामों की शुरुआत के साथ थोड़ा कम आम हो गए हैं, लेकिन अभी भी कुछ स्थितियों में उपयोगी हैं (और अन्य स्थितियों में उपनाम के साथ संयुक्त हैं, जो एक जटिल प्रकार के व्यक्तिगत घटकों जैसे कि फ़ंक्शन पॉइंटर को प्राप्त करने के लिए बहुत उपयोगी हो सकते हैं )। वे आमतौर पर अपने प्रकार के उपनाम के लिए नाम type
उपयोग करते हैं।
template<typename T>
struct TemplateTypedef {
typedef T type;
}
TemplateTypedef<int>::type i; // i is an int.
यह अक्सर एक या अधिक मापदंडों को परिभाषित करने वाले उपनाम प्रदान करने के लिए कई टेम्पलेट मापदंडों के साथ प्रकार के साथ उपयोग किया जाता था।
template<typename T, size_t SZ, size_t D>
class Array { /* ... */ };
template<typename T, size_t SZ>
struct OneDArray {
typedef Array<T, SZ, 1> type;
};
template<typename T, size_t SZ>
struct TwoDArray {
typedef Array<T, SZ, 2> type;
};
template<typename T>
struct MonoDisplayLine {
typedef Array<T, 80, 1> type;
};
OneDArray<int, 3>::type arr1i; // arr1i is an Array<int, 3, 1>.
TwoDArray<short, 5>::type arr2s; // arr2s is an Array<short, 5, 2>.
MonoDisplayLine<char>::type arr3c; // arr3c is an Array<char, 80, 1>.
स्थिर वर्ग के सदस्य
एक वर्ग को static
सदस्य रखने की भी अनुमति है, जो कि चर या कार्य हो सकते हैं। इन्हें वर्ग के दायरे में माना जाता है, लेकिन इन्हें सामान्य सदस्यों के रूप में नहीं माना जाता है; उनके पास स्थिर भंडारण अवधि है (वे कार्यक्रम की शुरुआत से अंत तक मौजूद हैं), वर्ग के एक विशेष उदाहरण से बंधे नहीं हैं, और पूरे वर्ग के लिए केवल एक प्रति मौजूद है।
class Example {
static int num_instances; // Static data member (static member variable).
int i; // Non-static member variable.
public:
static std::string static_str; // Static data member (static member variable).
static int static_func(); // Static member function.
// Non-static member functions can modify static member variables.
Example() { ++num_instances; }
void set_str(const std::string& str);
};
int Example::num_instances;
std::string Example::static_str = "Hello.";
// ...
Example one, two, three;
// Each Example has its own "i", such that:
// (&one.i != &two.i)
// (&one.i != &three.i)
// (&two.i != &three.i).
// All three Examples share "num_instances", such that:
// (&one.num_instances == &two.num_instances)
// (&one.num_instances == &three.num_instances)
// (&two.num_instances == &three.num_instances)
स्थिर सदस्य चर को केवल कक्षा के अंदर परिभाषित नहीं माना जाता है, केवल घोषित किया जाता है और इस प्रकार कक्षा की परिभाषा के बाहर उनकी परिभाषा होती है; प्रोग्रामर को अनुमति दी जाती है, लेकिन उनकी परिभाषा में स्थिर चर को शुरू करने की अनुमति नहीं है। सदस्य चर को परिभाषित करते समय, कीवर्ड static
को छोड़ दिया जाता है।
class Example {
static int num_instances; // Declaration.
public:
static std::string static_str; // Declaration.
// ...
};
int Example::num_instances; // Definition. Zero-initialised.
std::string Example::static_str = "Hello."; // Definition.
इसके कारण, स्थिर चर अपूर्ण प्रकार ( void
अलावा) हो सकते हैं, जब तक कि उन्हें बाद में पूर्ण प्रकार के रूप में परिभाषित किया जाता है।
struct ForwardDeclared;
class ExIncomplete {
static ForwardDeclared fd;
static ExIncomplete i_contain_myself;
static int an_array[];
};
struct ForwardDeclared {};
ForwardDeclared ExIncomplete::fd;
ExIncomplete ExIncomplete::i_contain_myself;
int ExIncomplete::an_array[5];
स्टैटिक सदस्य फ़ंक्शंस को सामान्य परिभाषा फ़ंक्शंस के साथ, क्लास परिभाषा के अंदर या बाहर परिभाषित किया जा सकता है। स्थिर सदस्य चरों के साथ, तब जब वर्ग परिभाषा के बाहर स्थैतिक सदस्य कार्यों को परिभाषित करते समय खोजशब्द static
को छोड़ दिया जाता है।
// For Example above, either...
class Example {
// ...
public:
static int static_func() { return num_instances; }
// ...
void set_str(const std::string& str) { static_str = str; }
};
// Or...
class Example { /* ... */ };
int Example::static_func() { return num_instances; }
void Example::set_str(const std::string& str) { static_str = str; }
यदि एक स्थिर सदस्य चर const
घोषित किया गया है, लेकिन volatile
नहीं है, और यह एक अभिन्न या गणना का प्रकार है, तो इसे वर्ग परिभाषा के अंदर घोषित किया जा सकता है।
enum E { VAL = 5 };
struct ExConst {
const static int ci = 5; // Good.
static const E ce = VAL; // Good.
const static double cd = 5; // Error.
static const volatile int cvi = 5; // Error.
const static double good_cd;
static const volatile int good_cvi;
};
const double ExConst::good_cd = 5; // Good.
const volatile int ExConst::good_cvi = 5; // Good.
C ++ 11 के अनुसार, LiteralType
टाइप प्रकारों के स्थिर सदस्य चर (प्रकार जो संकलित समय के अनुसार बनाए जा सकते हैं, constexpr
नियमों के अनुसार) को भी constexpr
रूप में घोषित किया जा सकता है; यदि हां, तो उन्हें वर्ग परिभाषा के भीतर आरम्भ किया जाना चाहिए।
struct ExConstexpr {
constexpr static int ci = 5; // Good.
static constexpr double cd = 5; // Good.
constexpr static int carr[] = { 1, 1, 2 }; // Good.
static constexpr ConstexprConstructibleClass c{}; // Good.
constexpr static int bad_ci; // Error.
};
constexpr int ExConstexpr::bad_ci = 5; // Still an error.
एक तो const
या constexpr
रूप से प्रयुक्त ओडीआर स्थिर सदस्य चर रहा है (अनौपचारिक रूप से, अगर यह इसका पता ले लिया है या एक संदर्भ को सौंपा गया है), तो यह अभी भी एक अलग परिभाषा होनी चाहिए, वर्ग परिभाषा के बाहर। इस परिभाषा में इनिशियलाइज़र को शामिल करने की अनुमति नहीं है।
struct ExODR {
static const int odr_used = 5;
};
// const int ExODR::odr_used;
const int* odr_user = & ExODR::odr_used; // Error; uncomment above line to resolve.
चूंकि स्थिर सदस्य किसी दिए गए उदाहरण से बंधे नहीं हैं, इसलिए उन्हें स्कोप ऑपरेटर, ::
का उपयोग करके एक्सेस किया जा सकता है।
std::string str = Example::static_str;
उन्हें भी एक्सेस किया जा सकता है जैसे कि वे सामान्य, गैर-स्थैतिक सदस्य थे। यह ऐतिहासिक महत्व का है, लेकिन किसी सदस्य के स्थिर या गैर-स्थिर होने पर भ्रम को रोकने के लिए गुंजाइश ऑपरेटर की तुलना में आमतौर पर कम उपयोग किया जाता है।
Example ex;
std::string rts = ex.static_str;
गैर-स्थैतिक वर्ग के सदस्यों के साथ वर्ग सदस्य अपने दायरे को योग्य किए बिना स्थिर सदस्यों तक पहुँचने में सक्षम हैं।
class ExTwo {
static int num_instances;
int my_num;
public:
ExTwo() : my_num(num_instances++) {}
static int get_total_instances() { return num_instances; }
int get_instance_number() const { return my_num; }
};
int ExTwo::num_instances;
वे mutable
नहीं हो सकते, और न ही उन्हें होने की आवश्यकता होगी; चूंकि वे किसी दिए गए उदाहरण से बंधे नहीं हैं, चाहे कोई उदाहरण हो या स्थिर न हो, स्थैतिक सदस्यों को प्रभावित नहीं करता है।
struct ExDontNeedMutable {
int immuta;
mutable int muta;
static int i;
ExDontNeedMutable() : immuta(-5), muta(-5) {}
};
int ExDontNeedMutable::i;
// ...
const ExDontNeedMutable dnm;
dnm.immuta = 5; // Error: Can't modify read-only object.
dnm.muta = 5; // Good. Mutable fields of const objects can be written.
dnm.i = 5; // Good. Static members can be written regardless of an instance's const-ness.
स्टैटिक सदस्य गैर-स्थैतिक सदस्यों की तरह ही एक्सेस मॉडिफायरों का सम्मान करते हैं।
class ExAccess {
static int prv_int;
protected:
static int pro_int;
public:
static int pub_int;
};
int ExAccess::prv_int;
int ExAccess::pro_int;
int ExAccess::pub_int;
// ...
int x1 = ExAccess::prv_int; // Error: int ExAccess::prv_int is private.
int x2 = ExAccess::pro_int; // Error: int ExAccess::pro_int is protected.
int x3 = ExAccess::pub_int; // Good.
चूंकि वे दिए गए उदाहरण से बंधे नहीं हैं, स्थिर सदस्य कार्यों में this
सूचक नहीं है; इसके कारण, वे तब तक गैर-स्थैतिक सदस्य चर का उपयोग नहीं कर सकते जब तक कि कोई उदाहरण न दिया गया हो।
class ExInstanceRequired {
int i;
public:
ExInstanceRequired() : i(0) {}
static void bad_mutate() { ++i *= 5; } // Error.
static void good_mutate(ExInstanceRequired& e) { ++e.i *= 5; } // Good.
};
this
पॉइंटर न होने के कारण, उनके पते पॉइंटर्स-टू-मेंबर-फ़ंक्शंस में संग्रहीत नहीं किए जा सकते हैं, और इसके बजाय सामान्य पॉइंटर्स-टू-फ़ंक्शंस में संग्रहीत किए जाते हैं।
struct ExPointer {
void nsfunc() {}
static void sfunc() {}
};
typedef void (ExPointer::* mem_f_ptr)();
typedef void (*f_ptr)();
mem_f_ptr p_sf = &ExPointer::sfunc; // Error.
f_ptr p_sf = &ExPointer::sfunc; // Good.
this
पॉइंटर के न होने के कारण, वे const
या volatile
नहीं हो सकते हैं, न ही उनके पास रेफ-क्वालिफायर हो सकते हैं। वे आभासी भी नहीं हो सकते।
struct ExCVQualifiersAndVirtual {
static void func() {} // Good.
static void cfunc() const {} // Error.
static void vfunc() volatile {} // Error.
static void cvfunc() const volatile {} // Error.
static void rfunc() & {} // Error.
static void rvfunc() && {} // Error.
virtual static void vsfunc() {} // Error.
static virtual void svfunc() {} // Error.
};
चूंकि वे दिए गए उदाहरण से बंधे नहीं हैं, स्थिर सदस्य चर को प्रभावी रूप से विशेष वैश्विक चर के रूप में माना जाता है; वे तब बनते हैं जब प्रोग्राम शुरू होता है, और तब नष्ट हो जाता है जब वह बाहर निकलता है, भले ही कक्षा का कोई भी उदाहरण वास्तव में मौजूद हो। प्रत्येक स्थिर सदस्य चर की केवल एक प्रति मौजूद है (जब तक कि चर को thread_local
घोषित नहीं किया thread_local
(C ++ 11 या बाद में), उस स्थिति में प्रति थ्रेड में एक प्रति है)।
स्टैटिक मेंबर वेरिएबल्स में क्लास की तरह ही लिंकेज होता है, चाहे क्लास में एक्सटर्नल या इंटरनल लिंकेज हो। स्थानीय वर्गों और अनाम वर्गों को स्थिर सदस्य रखने की अनुमति नहीं है।
गैर-स्थैतिक सदस्य कार्य
एक वर्ग में गैर-स्थैतिक सदस्य कार्य हो सकते हैं , जो वर्ग के व्यक्तिगत उदाहरणों पर काम करते हैं।
class CL {
public:
void member_function() {}
};
इन कार्यों को कक्षा के एक उदाहरण पर बुलाया जाता है, जैसे:
CL instance;
instance.member_function();
उन्हें कक्षा की परिभाषा के अंदर या बाहर परिभाषित किया जा सकता है; यदि उन्हें बाहर से परिभाषित किया जाता है, तो उन्हें कक्षा के दायरे में निर्दिष्ट किया जाता है।
struct ST {
void defined_inside() {}
void defined_outside();
};
void ST::defined_outside() {}
वे सीवी-योग्य और / या रेफ-योग्य हो सकते हैं , यह प्रभावित करते हैं कि वे उस उदाहरण को कैसे देखते हैं जिसे वे कहते हैं; फ़ंक्शन उदाहरण को निर्दिष्ट cv-qualifier (s) के रूप में देखेगा, यदि कोई हो। कौन सा संस्करण कहा जाता है, उदाहरण के cv-क्वालिफायर पर आधारित होगा। यदि उदाहरण के रूप में एक ही cv-क्वालिफायर के साथ कोई संस्करण नहीं है, तो उपलब्ध होने पर अधिक cv-योग्य संस्करण कहा जाएगा।
struct CVQualifiers {
void func() {} // 1: Instance is non-cv-qualified.
void func() const {} // 2: Instance is const.
void cv_only() const volatile {}
};
CVQualifiers non_cv_instance;
const CVQualifiers c_instance;
non_cv_instance.func(); // Calls #1.
c_instance.func(); // Calls #2.
non_cv_instance.cv_only(); // Calls const volatile version.
c_instance.cv_only(); // Calls const volatile version.
सदस्य फ़ंक्शन रेफ-क्वालिफायर इंगित करते हैं कि फ़ंक्शन को रिव्यू इंस्टेंसेस पर बुलाया जाना है या नहीं, और फ़ंक्शन सीवी-क्वालिफायर के समान सिंटैक्स का उपयोग करें।
struct RefQualifiers {
void func() & {} // 1: Called on normal instances.
void func() && {} // 2: Called on rvalue (temporary) instances.
};
RefQualifiers rf;
rf.func(); // Calls #1.
RefQualifiers{}.func(); // Calls #2.
यदि आवश्यक हो तो सीवी-क्वालिफायर और रेफ-क्वालिफायर को भी जोड़ा जा सकता है।
struct BothCVAndRef {
void func() const& {} // Called on normal instances. Sees instance as const.
void func() && {} // Called on temporary instances.
};
वे आभासी भी हो सकते हैं; यह बहुरूपता के लिए मौलिक है, और एक बाल वर्ग (तों) को अपनी स्वयं की कार्यक्षमता की आपूर्ति करते हुए मूल वर्ग के समान इंटरफ़ेस प्रदान करने की अनुमति देता है।
struct Base {
virtual void func() {}
};
struct Derived {
virtual void func() {}
};
Base* bp = new Base;
Base* dp = new Derived;
bp.func(); // Calls Base::func().
dp.func(); // Calls Derived::func().
अधिक जानकारी के लिए, यहाँ देखें।
गति / वर्ग
struct
की अनुमति है (प्रकार का कोई नाम नहीं है)
void foo()
{
struct /* No name */ {
float x;
float y;
} point;
point.x = 42;
}
या
struct Circle
{
struct /* No name */ {
float x;
float y;
} center; // but a member name
float radius;
};
और बादमें
Circle circle;
circle.center.x = 42.f;
लेकिन अनाम struct
नहीं (अनाम प्रकार और अनाम वस्तु)
struct InvalidCircle
{
struct /* No name */ {
float centerX;
float centerY;
}; // No member either.
float radius;
};
नोट: कुछ संकलक अनाम struct
को विस्तार के रूप में अनुमति देते हैं।
लाम्बा को एक विशेष अनाम
struct
रूप में देखा जा सकता है।decltype
अनामstruct
के प्रकार को पुनः प्राप्त करने की अनुमति देता है:decltype(circle.point) otherPoint;
अनाम
struct
उदाहरण टेम्पलेट विधि का पैरामीटर हो सकता है:void print_square_coordinates() { const struct {float x; float y;} points[] = { {-1, -1}, {-1, 1}, {1, -1}, {1, 1} }; // for range relies on `template <class T, std::size_t N> std::begin(T (&)[N])` for (const auto& point : points) { std::cout << "{" << point.x << ", " << point.y << "}\n"; } decltype(points[0]) topRightCorner{1, 1}; auto it = std::find(points, points + 4, topRightCorner); std::cout << "top right corner is the " << 1 + std::distance(points, it) << "th\n"; }