ऐसा लगता है कि यह एक डुप्लिकेट होगा, लेकिन शायद यह इतना स्पष्ट है कि इसे नहीं पूछा गया है ...
क्या यह जाँचने का सही तरीका है कि C++ क्लास में एक वेरिएबल (पॉइंटर नहीं) को इनिशियलाइज़ किया गया है या नहीं?
class MyClass
{
void SomeMethod();
char mCharacter;
double mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter )
{
// do something with mCharacter.
}
if ( ! mDecimal )
{
// define mDecimal.
}
}
13 जवाब
एक चर की सामग्री की जाँच करने का कोई तरीका अपरिभाषित है या नहीं। सबसे अच्छी चीज जो आप कर सकते हैं वह है सिग्नल/सेंटीनेल वैल्यू (उदाहरण के लिए कंस्ट्रक्टर में) असाइन करना यह इंगित करने के लिए कि आगे इनिशियलाइज़ेशन करने की आवश्यकता होगी।
std::optional
का उपयोग करना एक bool
("सिग्नल") के समान है जो यह चिन्हित करता है कि वेरिएबल को इनिशियलाइज़ किया गया है या नहीं।
वेरिएबल जो परिभाषित नहीं है, संकलन त्रुटि का कारण बनेगा।
आप जो पूछ रहे हैं वह यह जांचने के बारे में है कि क्या यह आरंभिक है। लेकिन इनिशियलाइज़ेशन सिर्फ एक वैल्यू है, जिसे आपको कंस्ट्रक्टर में चुनना और असाइन करना चाहिए।
उदाहरण के लिए:
class MyClass
{
MyClass() : mCharacter('0'), mDecimal(-1.0){};
void SomeMethod();
char mCharacter;
double mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter != '0')
{
// touched after the constructor
// do something with mCharacter.
}
if ( mDecimal != -1.0 )
{
// touched after the constructor
// define mDecimal.
}
}
आपको एक डिफ़ॉल्ट मान के लिए प्रारंभ करना चाहिए जिसका अर्थ निश्चित रूप से आपके तर्क के संदर्भ में कुछ होगा।
आपके अनुप्रयोगों के आधार पर (और विशेष रूप से यदि आप पहले से ही बूस्ट का उपयोग कर रहे हैं), तो हो सकता है कि आप boost::optional
.
(अद्यतन: C++17 के अनुसार, वैकल्पिक अब मानक पुस्तकालय का हिस्सा है, जैसा कि std::optional
)
इसमें वह संपत्ति है जिसकी आप तलाश कर रहे हैं, यह ट्रैक करते हुए कि स्लॉट वास्तव में एक मूल्य रखता है या नहीं। डिफ़ॉल्ट रूप से इसका निर्माण किसी मान को न रखने और असत्य का मूल्यांकन करने के लिए किया जाता है, लेकिन यदि यह सत्य का मूल्यांकन करता है तो आपको इसे डीरेफ़रेंस करने और लपेटा हुआ मान प्राप्त करने की अनुमति है।
class MyClass
{
void SomeMethod();
optional<char> mCharacter;
optional<double> mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter )
{
// do something with *mCharacter.
// (note you must use the dereference operator)
}
if ( ! mDecimal )
{
// call mDecimal.reset(expression)
// (this is how you assign an optional)
}
}
अधिक उदाहरण बूस्ट दस्तावेज़ों में हैं< /ए>.
C++17 के साथ आप std::optional
< का उपयोग कर सकते हैं /a> यह जाँचने के लिए कि क्या कोई वेरिएबल इनिशियलाइज़ किया गया है:
#include <optional>
#include <iostream> // needed only for std::cout
int main() {
std::optional<int> variable;
if (!variable) {
std::cout << "variable is NOT initialized\n";
}
variable = 3;
if (variable) {
std::cout << "variable IS initialized and is set to " << *variable << '\n';
}
return 0;
}
यह आउटपुट का उत्पादन करेगा:
variable is NOT initialized
variable IS initialized and is set to 3
आपके द्वारा प्रदान किए गए कोड में std::optional
का उपयोग करने के लिए, आपको शामिल करना होगा <optional>
मानक पुस्तकालय शीर्षलेख और संबंधित चर घोषणाओं में std::optional<...>
जोड़ें:
#include <optional>
class MyClass
{
void SomeMethod();
std::optional<char> mCharacter;
std::optional<double> mDecimal;
};
void MyClass::SomeMethod()
{
if ( mCharacter )
{
std::cout << *mCharacter; // do something with mCharacter.
}
if ( ! mDecimal )
{
mDecimal = 3.14159; // define mDecimal.
}
}
C++-11 या बूस्ट लिब के साथ आप स्मार्ट पॉइंटर्स का उपयोग करके वेरिएबल को स्टोर करने पर विचार कर सकते हैं। इस एमवीई पर विचार करें जहां toString()
व्यवहार bar
के आरंभिक होने या न होने पर निर्भर करता है:
#include <memory>
#include <sstream>
class Foo {
private:
std::shared_ptr<int> bar;
public:
Foo() {}
void setBar(int bar) {
this->bar = std::make_shared<int>(bar);
}
std::string toString() const {
std::ostringstream ss;
if (bar) // bar was set
ss << *bar;
else // bar was never set
ss << "unset";
return ss.str();
}
};
इस कोड का उपयोग करना
Foo f;
std::cout << f.toString() << std::endl;
f.setBar(42);
std::cout << f.toString() << std::endl;
उत्पादन करता है
unset
42
Foo f; if (f) ...
-- यह मेरे लिए काम नहीं कर रहा है, लेकिन शायद कोई और तरीका है?
std::optional
का उपयोग करके एल्मर के समाधान का उपयोग करें।
std::optional
जैसा कि एल्मर ने सुझाव दिया था, यह दावा करता है कि आप हमेशा जांच सकते हैं कि चर ने संकेत दिया है या नहीं। आपके द्वारा इंगित ध्वज का उपयोग करके भूलने का कोई मौका नहीं है। प्लस: चर को सीधे एक्सेस करने के बजाय सेटर्स के उपयोग को लागू करने की आवश्यकता नहीं है।
डिफ़ॉल्ट रूप से, नहीं, आप यह नहीं जान सकते कि कोई चर (या सूचक) प्रारंभ किया गया है या नहीं। हालांकि, चूंकि हर कोई आपको "आसान" या "सामान्य" दृष्टिकोण बता रहा है, इसलिए मैं आपको सोचने के लिए कुछ और दूंगा। यहां बताया गया है कि आप कुछ इस तरह का ट्रैक कैसे रख सकते हैं (नहीं, मैं व्यक्तिगत रूप से ऐसा कभी नहीं करूंगा, लेकिन शायद आपको मुझसे अलग जरूरतें हैं)।
class MyVeryCoolInteger
{
public:
MyVeryCoolInteger() : m_initialized(false) {}
MyVeryCoolInteger& operator=(const int integer)
{
m_initialized = true;
m_int = integer;
return *this;
}
int value()
{
return m_int;
}
bool isInitialized()
{
return m_initialized;
}
private:
int m_int;
bool m_initialized;
};
boost::optional
कहा जाता है। (मेरा उत्तर देखें।) यदि आपके पास ऐसी स्थिति है तो इसका उपयोग करने में कुछ भी गलत नहीं है। हालांकि निश्चित रूप से यदि आपके पास लाखों पूर्णांक हैं, तो यह "कोई मूल्य नहीं" इंगित करने के लिए एक जादू मूल्य चुनने के लिए अच्छी तरह से भुगतान कर सकता है।
यह जांचने का कोई उचित तरीका नहीं है कि कोई मान प्रारंभ किया गया है या नहीं।
यदि आप इस बात की परवाह करते हैं कि क्या कुछ आरंभ किया गया है, तो इसकी जांच करने की कोशिश करने के बजाय, यह सुनिश्चित करने के लिए कि वे हमेशा प्रारंभ किए गए हैं और इसके साथ किया जाना सुनिश्चित करने के लिए कोड डालें।
यदि आप यह जांचना चाहते हैं कि सदस्य चर प्रारंभ किए गए हैं या नहीं, तो आप इसे कन्स्ट्रक्टर में सेंटीनेल मान निर्दिष्ट करके कर सकते हैं। प्रहरी मूल्यों को उन मूल्यों के रूप में चुनें जो उस चर के सामान्य उपयोग में कभी नहीं होंगे। यदि एक चर पूरी श्रेणी को मान्य माना जाता है, तो आप यह इंगित करने के लिए एक बूलियन बना सकते हैं कि इसे प्रारंभ किया गया है या नहीं।
#include <limits>
class MyClass
{
void SomeMethod();
char mCharacter;
bool isCharacterInitialized;
double mDecimal;
MyClass()
: isCharacterInitialized(false)
, mDecimal( std::numeric_limits<double>::quiet_NaN() )
{}
};
void MyClass::SomeMethod()
{
if ( isCharacterInitialized == false )
{
// do something with mCharacter.
}
if ( mDecimal != mDecimal ) // if true, mDecimal == NaN
{
// define mDecimal.
}
}
चूंकि MyClass
एक POD वर्ग प्रकार है, इसलिए जब आप MyClass
का एक गैर-स्थिर उदाहरण बनाते हैं, तो उन गैर-स्थिर डेटा सदस्यों के पास अनिश्चित प्रारंभिक मान होंगे, इसलिए नहीं, यह जांचने का एक वैध तरीका नहीं है कि क्या उन्हें एक विशिष्ट गैर-शून्य मान में प्रारंभ किया गया है ... आप मूल रूप से मान रहे हैं कि वे शून्य-प्रारंभिक होंगे, जो ऐसा नहीं होने वाला है क्योंकि आपने उन्हें कन्स्ट्रक्टर में मूल्य-प्रारंभ नहीं किया है।
यदि आप अपनी कक्षा के गैर-स्थिर डेटा सदस्यों को शून्य-प्रारंभ करना चाहते हैं, तो प्रारंभिक सूची और कक्षा-निर्माता बनाना सबसे अच्छा होगा। उदाहरण के लिए:
class MyClass
{
void SomeMethod();
char mCharacter;
double mDecimal;
public:
MyClass();
};
MyClass::MyClass(): mCharacter(0), mDecimal(0) {}
मूल्य से ऊपर के कंस्ट्रक्टर में इनिशियलाइज़ेशन सूची आपके डेटा-सदस्यों को शून्य पर आरंभ करती है। अब आप ठीक से मान सकते हैं कि mCharacter
और mDecimal
के लिए कोई भी गैर-शून्य मान विशेष रूप से आपके द्वारा आपके कोड में कहीं और निर्धारित किया गया होगा, और इसमें गैर-शून्य मान शामिल हैं जिन पर आप ठीक से कार्य कर सकते हैं।
MyClass
के लिए डिफॉल्ट कंस्ट्रक्टर, चूंकि यह ओपी के मूल संस्करण में किसी भी वेरिएबल को वैल्यू-इनिशियलाइज़ नहीं करता है, डेटा सदस्यों के लिए रैंडम वैल्यू बनाएगा ... एक रैंडम वैल्यू (यानी, मेमोरी में पहले से मौजूद वैल्यू) है POD डेटा-प्रकार के लिए डिफ़ॉल्ट-आरंभीकरण।
सी ++ भाषा में यह जांचने का कोई तरीका नहीं है कि एक चर प्रारंभ किया गया है या नहीं (हालांकि रचनाकारों के साथ वर्ग प्रकार स्वचालित रूप से प्रारंभ हो जाएंगे)।
इसके बजाय, आपको जो करना है वह कंस्ट्रक्टर प्रदान करना है जो आपकी कक्षा को एक वैध स्थिति में आरंभ करता है। स्टेटिक कोड चेकर्स (और संभवत: कुछ कंपाइलर) आपको कंस्ट्रक्टर्स में लापता चर खोजने में मदद कर सकते हैं। इस तरह आपको फर्जी स्थिति में होने के बारे में चिंता करने की नहीं है और आपके तरीके में if
चेक पूरी तरह से समाप्त हो सकते हैं।
उदाहरण के लिए यदि आप वर्णों के बजाय तार का उपयोग करते हैं, तो आप ऐसा कुछ करने में सक्षम हो सकते हैं:
//a is a string of length 1
string a;
//b is the char in which we'll put the char stored in a
char b;
bool isInitialized(){
if(a.length() != NULL){
b = a[0];
return true;
}else return false;
}
आप एक अभिकथन में चर का संदर्भ दे सकते हैं और फिर -fsanitize=address
के साथ निर्माण कर सकते हैं:
void foo (int32_t& i) {
// Assertion will trigger address sanitizer if not initialized:
assert(static_cast<int64_t>(i) != INT64_MAX);
}
यह प्रोग्राम को स्टैक ट्रेस (अपरिभाषित व्यवहार के विपरीत) के साथ विश्वसनीय रूप से क्रैश करने का कारण बनता है।
अगर आपको बूस्ट और c++17 पसंद नहीं है, तो google c++ lib Abseil इसे महसूस करने का दूसरा तरीका है। absl::वैकल्पिक बिल्कुल std :: वैकल्पिक की तरह है।
#include <absl/types/optional.h>
class MyClass
{
void SomeMethod();
absl::optional<char> mCharacter;
absl::optional<double> mDecimal;
};
void MyClass::SomeMethod()
{
if (mCharacter)
{
// do something with mCharacter.
}
if (!mDecimal)
{
// define mDecimal.
}
}
संबंधित सवाल
जुड़े हुए प्रश्न
नए सवाल
c++
C ++ एक सामान्य-प्रयोजन प्रोग्रामिंग भाषा है। यह मूल रूप से C के विस्तार के रूप में डिज़ाइन किया गया था और इसमें एक समान सिंटैक्स है, लेकिन यह अब पूरी तरह से अलग भाषा है। C ++ कंपाइलर के साथ संकलित कोड के बारे में प्रश्नों के लिए इस टैग का उपयोग करें। विशिष्ट मानक संशोधन [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] या [C ++ 23], आदि से संबंधित प्रश्नों के लिए संस्करण-विशिष्ट टैग का उपयोग करें। ।