अंतिम अद्यतन:2026-10-12
C++ const constant और qualifier¶
लेखक के बारे में¶
WeChat सार्वजनिक अकाउंट:

1.const का अर्थ¶
const टाइप उस टाइप को कहते हैं जिसे type modifier const के साथ बताया गया हो, const टाइप के वेरिएबल या ऑब्जेक्ट की वैल्यू update नहीं की जा सकती।
2.const का काम¶
- constant परिभाषित किया जा सकता है
-
type checking
-
const constant और
#definemacro constant में अंतर:const constant का टाइप होता है, कंपाइलर सुरक्षा जाँच कर सकता है; #define macro का कोई डेटा टाइप नहीं होता, यह सिर्फ़ सरल string replacement है, सुरक्षा जाँच नहीं हो सकती।यहाँ की समस्या बताने के लिए दो जानकारों का धन्यवाद, देखें: issue -
const constant सभी टाइप सपोर्ट करता है।
- दूसरी स्थितियों में यह केवल एक
constqualified वेरिएबल है, इसे constant से न जोड़कर देखें।
-
-
modification से रोकना, सुरक्षा का काम, प्रोग्राम की robustness बढ़ाना
-
जगह बचाई जा सकती है, अनावश्यक memory allocation से बचा जा सकता है
- assembly के नज़रिए से const से परिभाषित constant केवल corresponding मेमोरी पता देता है,
#defineकी तरह immediate value नहीं। - const से परिभाषित constant प्रोग्राम चलने के दौरान केवल एक copy रखता है, जबकि
#defineसे परिभाषित constant मेमोरी में कई copy रखते हैं।
- assembly के नज़रिए से const से परिभाषित constant केवल corresponding मेमोरी पता देता है,
3.const ऑब्जेक्ट डिफ़ॉल्ट रूप से file-local¶
ध्यान दें: non-const वेरिएबल डिफ़ॉल्ट रूप से extern होते हैं। const वेरिएबल को दूसरी फ़ाइल में एक्सेस करने के लिए, फ़ाइल में उसे स्पष्ट रूप से extern निर्दिष्ट करना होगा।
const से qualify न हुए वेरिएबल की अलग-अलग फ़ाइलों में access
// file1.cpp
int ext;
// file2.cpp
#include<iostream>
extern int ext;
int main(){
std::cout<<(ext+10)<<std::endl;
}
const constant की अलग-अलग फ़ाइलों में access
// extern_file1.cpp
extern const int ext=12;
// extern_file2.cpp
#include<iostream>
extern const int ext;
int main(){
std::cout<<ext<<std::endl;
}
सारांश:
पाया जा सकता है कि const से qualify न हुए वेरिएबल को extern की स्पष्ट declaration की ज़रूरत नहीं! जबकि const constant को extern की स्पष्ट declaration चाहिए, और initialization भी! क्योंकि constant परिभाषित होने के बाद modify नहीं हो सकता, इसलिए परिभाषित करते समय ही initialize करना ज़रूरी है।
4.constant परिभाषित करना¶
const int b = 10;
b = 0; // error: assignment of read-only variable ‘b’
const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’
उपरोक्त में दो ग़लतियाँ हैं: + b constant है, बदला नहीं जा सकता! + i constant है, initialize करना ही होगा! (क्योंकि constant परिभाषित होने के बाद modify नहीं हो सकता, इसलिए परिभाषित करते समय ही initialize करना ज़रूरी है।)
5.पॉइंटर और const¶
पॉइंटर से जुड़ा const चार तरह का होता है:
const char * a; // 指向const对象的指针或者说指向常量的指针。
char const * a; // 同上
char * const a; // 指向类型对象的const指针。或者说常指针、const指针。
const char * const a; // 指向const对象的const指针。
सारांश:
अगर const*के बाईं ओर हो, तो const पॉइंटर द्वारा इंगित वेरिएबल को qualify करता है, यानी पॉइंटर की दिशा constant है;
अगर const*के दाईं ओर हो, तो const पॉइंटर को खुद qualify करता है, यानी पॉइंटर खुद constant है।
दूसरा पढ़ने का तरीका
अंग्रेज़ी को दाएँ से बाएँ पढ़ें, और to को विभाजक मानें, to से पहले पॉइंटर की विशेषता, to के बाद target की विशेषता
const char * p; //p is a pointer to const char
char const * p; //同上
char * const p; //p is a const pointer to char
const char * const p; //p is a const pointer to const char
जब इंगित target की विशेषता char हो, तो content पॉइंटर के ज़रिए बदला जा सकता है, जैसे: *char='y';
जब इंगित target की विशेषता const char हो, तो content पॉइंटर के ज़रिए बदला नहीं जा सकता
विस्तृत उपयोग नीचे है:
(1) constant की ओर पॉइंट करने वाला पॉइंटर
ptr एक int टाइप const ऑब्जेक्ट की ओर पॉइंट करने वाला पॉइंटर है, const से int टाइप परिभाषित होता है, यानी ptr के इंगित ऑब्जेक्ट का टाइप, ptr खुद नहीं, इसलिए ptr को initial value देने की ज़रूरत नहीं। लेकिन ptr के ज़रिए इंगित ऑब्जेक्ट की वैल्यू नहीं बदली जा सकती।
इसके अलावा, void* पॉइंटर से const ऑब्जेक्ट का पता स्टोर नहीं किया जा सकता, const ऑब्जेक्ट का पता const void* टाइप के पॉइंटर से ही स्टोर करना होगा।
एक और महत्वपूर्ण बात: non-const ऑब्जेक्ट का पता const ऑब्जेक्ट की ओर पॉइंट करने वाले पॉइंटर को दिया जा सकता है।
non-const ऑब्जेक्ट का पता const ऑब्जेक्ट के पॉइंटर को देना:
हम ptr पॉइंटर से val की वैल्यू नहीं बदल सकते, भले ही वह non-const ऑब्जेक्ट की ओर पॉइंट करता हो!
हम const ऑब्जेक्ट की ओर पॉइंट करने वाले पॉइंटर से मूल ऑब्जेक्ट नहीं बदल सकते, हालाँकि अगर वह पॉइंटर non-const ऑब्जेक्ट की ओर पॉइंट करे, तो दूसरे तरीकों से उसके इंगित ऑब्जेक्ट को बदला जा सकता है। const पॉइंटर के इंगित वैल्यू को बदला जा सकता है, लेकिन const ऑब्जेक्ट पॉइंटर से सीधे नहीं! नीचे की तरह बदलें:
सारांश:
1.constant की ओर पॉइंट करने वाले पॉइंटर के लिए, पॉइंटर से ऑब्जेक्ट की वैल्यू नहीं बदली जा सकती।
2.void*पॉइंटर से const ऑब्जेक्ट का पता स्टोर नहीं किया जा सकता, const ऑब्जेक्ट का पता const void*टाइप के पॉइंटर से ही स्टोर करना होगा।
3.non-const ऑब्जेक्ट का पता const ऑब्जेक्ट के पॉइंटर को दिया जा सकता है, अगर पॉइंटर के इंगित ऑब्जेक्ट की वैल्यू बदलनी हो, तो दूसरे तरीके से बदलना होगा, मौजूदा पॉइंटर से सीधे नहीं।
(2) constant पॉइंटर
const पॉइंटर को initialize करना ज़रूरी है, और const पॉइंटर की इंगित वैल्यू बदली जा सकती है, लेकिन दिशा नहीं बदली जा सकती।
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
ptr = &num1; // error! const指针不能修改指向!
cout<<*ptr<<endl;
}
कोड में compile error आता है: const पॉइंटर दिशा नहीं बदल सकता।
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
*ptr = 1;
cout<<*ptr<<endl;
}
कोड में कुछ नहीं होता, सामान्य रूप से 1 आउटपुट होता है।
आख़िर में, जब एक const constant का पता ptr को दिया जाता है, क्योंकि ptr एक वेरिएबल की ओर पॉइंट करता है, const constant की ओर नहीं, इसलिए error आता है, दिखता है: const int* -> int * error!
#include<iostream>
using namespace std;
int main(){
const int num=0;
int * const ptr=# // error! const int* -> int*
cout<<*ptr<<endl;
}
उपरोक्त को const int *ptr या const int *const ptr कर दें, दोनों सही चलेंगे!
(3)constant की ओर पॉइंट करने वाला constant पॉइंटर
पहली दो स्थितियाँ समझ लेने के बाद, नीचे वाली स्थिति समझना आसान है:
ptr एक const पॉइंटर है, और एक int टाइप के const ऑब्जेक्ट की ओर पॉइंट करता है।
6.फ़ंक्शन में const का उपयोग¶
const से फ़ंक्शन का return value qualify करना
यह const से सामान्य वेरिएबल और पॉइंटर की तरह ही है:
(1)const int
इसका अपने आप में कोई मतलब नहीं, क्योंकि parameter return खुद दूसरे वेरिएबल को assign होता है!
(2)const int*
पॉइंटर के इंगित content नहीं बदलता।
(3)int *const
पॉइंटर खुद नहीं बदलता।
const से फ़ंक्शन पैरामीटर qualify करना
(1)पास किया गया पैरामीटर और पॉइंटर खुद फ़ंक्शन में नहीं बदलते, इसका कोई मतलब नहीं!
यह दर्शाता है कि पैरामीटर फ़ंक्शन बॉडी में modify नहीं हो सकता, लेकिन यहाँ कोई मतलब नहीं, var खुद formal parameter है, फ़ंक्शन में नहीं बदलता। पास किया formal parameter पॉइंटर हो तो भी वैसा ही।
input पैरामीटर "pass by value" उपयोग करे, तो फ़ंक्शन अपने-आप temporary वेरिएबल बनाता है पैरामीटर copy करने के लिए, ऐसे input पैरामीटर की सुरक्षा की ज़रूरत ही नहीं, इसलिए const न लगाएँ।
(2)पैरामीटर पॉइंटर का इंगित content constant है, नहीं बदलता
इसमें src input पैरामीटर है, dst output पैरामीटर। src पर const लगाने के बाद, अगर फ़ंक्शन बॉडी का statement src का content बदलने की कोशिश करे, कंपाइलर error बताएगा। यही const लगाने के कामों में से एक है।
(3)पैरामीटर reference है, दक्षता बढ़ाने और modification रोकने के लिए।
non-inner डेटा टाइप के पैरामीटर के लिए, void func(A a) जैसे declare किए फ़ंक्शन की दक्षता कम होती है। क्योंकि फ़ंक्शन बॉडी में A टाइप का temporary ऑब्जेक्ट बनेगा पैरामीटर a copy करने के लिए, और temporary ऑब्जेक्ट के construction, copy, destruction में समय खपत होता है।
दक्षता बढ़ाने के लिए, फ़ंक्शन declaration को void func(A &a) किया जा सकता है, क्योंकि "pass by reference" सिर्फ़ पैरामीटर का उपनाम उधार लेता है, temporary ऑब्जेक्ट बनाने की ज़रूरत नहीं।
लेकिन फ़ंक्शन void func(A &a) की एक कमी है:
"pass by reference" से पैरामीटर a बदल सकता है, जो हम नहीं चाहते। इस समस्या का हल आसान है, const लगा दें, इसलिए फ़ंक्शन अंत में void func(const A &a) बनता है।
इसी तर्क से, क्या void func(int x) को दक्षता के लिए void func(const int &x) लिखना चाहिए? बिल्कुल ज़रूरत नहीं, क्योंकि inner डेटा टाइप के पैरामीटर में construction, destruction की प्रक्रिया नहीं होती, और copy भी बहुत तेज़ है, "pass by value" और "pass by reference" की दक्षता लगभग बराबर है।
सारांश:
1.non-inner डेटा टाइप के input पैरामीटर के लिए, "pass by value" को "const pass by reference" करना चाहिए, मक़सद दक्षता बढ़ाना है। जैसे void func(A a) को void func(const A &a) करना।
2.inner डेटा टाइप के input पैरामीटर के लिए, "pass by value" को "const pass by reference" न बदलें। वरना दक्षता बढ़ेगी नहीं और फ़ंक्शन की समझ घटेगी। जैसे void func(int x) को void func(const int &x) नहीं करना चाहिए।
ऊपर से दो interview सवाल हल हुए:
- अगर फ़ंक्शन को एक पॉइंटर पास करना हो, तो उस पॉइंटर पर const लगाना चाहिए या नहीं, const को पॉइंटर की अलग जगह लगाने से क्या अंतर है;
- अगर लिखे गए फ़ंक्शन में पास करना वाला पैरामीटर किसी complex टाइप का इंस्टेंस है, तो value पैरामीटर या reference पैरामीटर में क्या अंतर है, पास किए reference पैरामीटर पर कब const लगाना चाहिए।
7.क्लास में const का उपयोग¶
एक क्लास में, कोई भी फ़ंक्शन जो डेटा मेंबर नहीं बदलता, उसे const टाइप declare करना चाहिए। अगर const मेंबर फ़ंक्शन लिखते समय ग़लती से डेटा मेंबर बदल दें, या दूसरा non-const मेंबर फ़ंक्शन कॉल कर दें, तो कंपाइलर error बताएगा, जिससे प्रोग्राम की robustness बढ़ती है।
const कीवर्ड से declare किए मेंबर फ़ंक्शन को constant मेंबर फ़ंक्शन कहते हैं। केवल constant मेंबर फ़ंक्शन ही constant या constant ऑब्जेक्ट पर operation करने के योग्य हैं, const कीवर्ड से declare न किए मेंबर फ़ंक्शन से constant ऑब्जेक्ट पर operation नहीं किया जा सकता।
क्लास के const मेंबर वेरिएबल को initializer list से ही initialize करना होगा, जैसा नीचे दिखाया गया है:
class Apple{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
};
Apple::Apple(int i):apple_number(i)
{
}
const ऑब्जेक्ट केवल const मेंबर फ़ंक्शन एक्सेस कर सकता है, जबकि non-const ऑब्जेक्ट कोई भी मेंबर फ़ंक्शन एक्सेस कर सकता है, const मेंबर फ़ंक्शन समेत।
उदाहरण के लिए:
// apple.cpp
class Apple
{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
void take(int num) const;
int add();
int add(int num) const;
int getCount() const;
};
// apple.cpp
Apple::Apple(int i) : apple_number(i)
{
}
int Apple::add(int num)
{
take(num);
return 0;
}
int Apple::add(int num) const
{
take(num);
return 0;
}
void Apple::take(int num) const
{
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const
{
take(1);
// add(); // error
return apple_number;
}
int main()
{
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
// main.cpp
compile: bazel run basic_content/const/class_const/first_example:main
इस समय error आता है, ऊपर getCount() मेथड में एक add मेथड कॉल किया गया, जबकि add मेथड const से qualify नहीं है, इसलिए run में error। यानी const मेंबर फ़ंक्शन केवल const मेंबर फ़ंक्शन ही एक्सेस कर सकते हैं।
कॉल को इसमें बदलने पर:
इस समय, यह सिद्ध होता है कि const ऑब्जेक्ट केवल const मेंबर फ़ंक्शन एक्सेस कर सकता है।ऊपर बताई initializer list वाली तरीके के अलावा, const constant को नीचे के तरीकों से भी initialize किया जा सकता है:
पहला: constant की परिभाषा को static के साथ जोड़ना, यानी:
दूसरा: बाहर initialize करना:
बेशक, अगर आप c++11 से compile करें, तो परिभाषित करते ही initialize कर सकते हैं, सीधे लिखें:
दोनों c++11 में समर्थित हैं!
compile करते समय -std=c++11 लगा दें!
यहाँ static की बात आई, नीचे संक्षेप में कहते हैं:
C++ में, static मेंबर वेरिएबल को क्लास के अंदर initialize नहीं किया जा सकता। क्लास के अंदर केवल declaration होती है, definition क्लास definition के बाहर होनी ही चाहिए, आमतौर पर क्लास की implementation फ़ाइल में initialize किया जाता है।
क्लास में declare करें:
क्लास implementation फ़ाइल में उपयोग करें:
इस मामले में, c++11 में declare करते ही initialize नहीं किया जा सकता, यानी ऊपर वाला उपयोग तरीका।
संबंधित विषय¶
- C++ प्योर वर्चुअल फ़ंक्शन और एब्स्ट्रैक्ट क्लास | C++那些事
- C++ assert | C++那些事
- C++ bit-field | C++那些事
- C भाषा से C++ पॉलीमॉर्फ़िज़्म | C++那些事
- C++ const परिवार | C++那些事
- C++ dereference ऑपरेटर * | C++那些事
- C++ इनिशियलाइज़ेशन और असाइनमेंट | C++那些事
- C++ multiple return values लिखने के तरीके | C++那些事
- C++ mutable कीवर्ड | C++那些事
- C++ restrict और कंपाइलर अनुकूलन | C++那些事