विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ const constant और qualifier

लेखक के बारे में

WeChat सार्वजनिक अकाउंट:

wechat

1.const का अर्थ

const टाइप उस टाइप को कहते हैं जिसे type modifier const के साथ बताया गया हो, const टाइप के वेरिएबल या ऑब्जेक्ट की वैल्यू update नहीं की जा सकती।

2.const का काम

  • constant परिभाषित किया जा सकता है
const int a=100;
  • type checking

    • const constant और #define macro constant में अंतर:

      const constant का टाइप होता है, कंपाइलर सुरक्षा जाँच कर सकता है; #define macro का कोई डेटा टाइप नहीं होता, यह सिर्फ़ सरल string replacement है, सुरक्षा जाँच नहीं हो सकती।यहाँ की समस्या बताने के लिए दो जानकारों का धन्यवाद, देखें: issue

    • const constant सभी टाइप सपोर्ट करता है।

    • दूसरी स्थितियों में यह केवल एक const qualified वेरिएबल है, इसे constant से न जोड़कर देखें।
  • modification से रोकना, सुरक्षा का काम, प्रोग्राम की robustness बढ़ाना

void f(const int i){
    i++; // error!
}
  • जगह बचाई जा सकती है, अनावश्यक memory allocation से बचा जा सकता है

    • assembly के नज़रिए से const से परिभाषित constant केवल corresponding मेमोरी पता देता है, #define की तरह immediate value नहीं।
    • const से परिभाषित constant प्रोग्राम चलने के दौरान केवल एक copy रखता है, जबकि #define से परिभाषित constant मेमोरी में कई copy रखते हैं।

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
जब पॉइंटर पर const विशेषता लगाई जाए, तो पॉइंटर अपनी इंगित पता नहीं बदल सकता
जब इंगित target की विशेषता char हो, तो content पॉइंटर के ज़रिए बदला जा सकता है, जैसे: *char='y';
जब इंगित target की विशेषता const char हो, तो content पॉइंटर के ज़रिए बदला नहीं जा सकता

विस्तृत उपयोग नीचे है:

(1) constant की ओर पॉइंट करने वाला पॉइंटर

const int *ptr;
*ptr = 10; // error

ptr एक int टाइप const ऑब्जेक्ट की ओर पॉइंट करने वाला पॉइंटर है, const से int टाइप परिभाषित होता है, यानी ptr के इंगित ऑब्जेक्ट का टाइप, ptr खुद नहीं, इसलिए ptr को initial value देने की ज़रूरत नहीं। लेकिन ptr के ज़रिए इंगित ऑब्जेक्ट की वैल्यू नहीं बदली जा सकती।

इसके अलावा, void* पॉइंटर से const ऑब्जेक्ट का पता स्टोर नहीं किया जा सकता, const ऑब्जेक्ट का पता const void* टाइप के पॉइंटर से ही स्टोर करना होगा।

const int p = 10;
const void * vp = &p;
void *vp = &p; // error

एक और महत्वपूर्ण बात: non-const ऑब्जेक्ट का पता const ऑब्जेक्ट की ओर पॉइंट करने वाले पॉइंटर को दिया जा सकता है।

non-const ऑब्जेक्ट का पता const ऑब्जेक्ट के पॉइंटर को देना:

const int *ptr;
int val = 3;
ptr = &val; // ok

हम ptr पॉइंटर से val की वैल्यू नहीं बदल सकते, भले ही वह non-const ऑब्जेक्ट की ओर पॉइंट करता हो!

हम const ऑब्जेक्ट की ओर पॉइंट करने वाले पॉइंटर से मूल ऑब्जेक्ट नहीं बदल सकते, हालाँकि अगर वह पॉइंटर non-const ऑब्जेक्ट की ओर पॉइंट करे, तो दूसरे तरीकों से उसके इंगित ऑब्जेक्ट को बदला जा सकता है। const पॉइंटर के इंगित वैल्यू को बदला जा सकता है, लेकिन const ऑब्जेक्ट पॉइंटर से सीधे नहीं! नीचे की तरह बदलें:

int *ptr1 = &val;
*ptr1=4;
cout<<*ptr<<endl;

सारांश:
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=&num; // 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=&num; // 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=&num; // error! const int* -> int*
    cout<<*ptr<<endl;
}

उपरोक्त को const int *ptr या const int *const ptr कर दें, दोनों सही चलेंगे!

(3)constant की ओर पॉइंट करने वाला constant पॉइंटर

पहली दो स्थितियाँ समझ लेने के बाद, नीचे वाली स्थिति समझना आसान है:

const int p = 3;
const int * const ptr = &p; 

ptr एक const पॉइंटर है, और एक int टाइप के const ऑब्जेक्ट की ओर पॉइंट करता है।

6.फ़ंक्शन में const का उपयोग

const से फ़ंक्शन का return value qualify करना

यह const से सामान्य वेरिएबल और पॉइंटर की तरह ही है:

(1)const int

const int func1();

इसका अपने आप में कोई मतलब नहीं, क्योंकि parameter return खुद दूसरे वेरिएबल को assign होता है!

(2)const int*

const int* func2();

पॉइंटर के इंगित content नहीं बदलता।

(3)int *const

int *const func2();

पॉइंटर खुद नहीं बदलता।

const से फ़ंक्शन पैरामीटर qualify करना

(1)पास किया गया पैरामीटर और पॉइंटर खुद फ़ंक्शन में नहीं बदलते, इसका कोई मतलब नहीं!

void func(const int var); // 传递过来的参数不可变
void func(int *const var); // 指针本身不可变

यह दर्शाता है कि पैरामीटर फ़ंक्शन बॉडी में modify नहीं हो सकता, लेकिन यहाँ कोई मतलब नहीं, var खुद formal parameter है, फ़ंक्शन में नहीं बदलता। पास किया formal parameter पॉइंटर हो तो भी वैसा ही।

input पैरामीटर "pass by value" उपयोग करे, तो फ़ंक्शन अपने-आप temporary वेरिएबल बनाता है पैरामीटर copy करने के लिए, ऐसे input पैरामीटर की सुरक्षा की ज़रूरत ही नहीं, इसलिए const न लगाएँ।

(2)पैरामीटर पॉइंटर का इंगित content constant है, नहीं बदलता

void StringCopy(char *dst, const char *src);

इसमें src input पैरामीटर है, dst output पैरामीटर। src पर const लगाने के बाद, अगर फ़ंक्शन बॉडी का statement src का content बदलने की कोशिश करे, कंपाइलर error बताएगा। यही const लगाने के कामों में से एक है।

(3)पैरामीटर reference है, दक्षता बढ़ाने और modification रोकने के लिए।

void func(const A &a)

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 Apple b(3);
b.add(); // error
इस समय, यह सिद्ध होता है कि const ऑब्जेक्ट केवल const मेंबर फ़ंक्शन एक्सेस कर सकता है।

ऊपर बताई initializer list वाली तरीके के अलावा, const constant को नीचे के तरीकों से भी initialize किया जा सकता है:

पहला: constant की परिभाषा को static के साथ जोड़ना, यानी:

static const int apple_number

दूसरा: बाहर initialize करना:

const int Apple::apple_number=10;

बेशक, अगर आप c++11 से compile करें, तो परिभाषित करते ही initialize कर सकते हैं, सीधे लिखें:

static const int apple_number=10;
// 或者
const int apple_number=10;

दोनों c++11 में समर्थित हैं!

compile करते समय -std=c++11 लगा दें!

यहाँ static की बात आई, नीचे संक्षेप में कहते हैं:

C++ में, static मेंबर वेरिएबल को क्लास के अंदर initialize नहीं किया जा सकता। क्लास के अंदर केवल declaration होती है, definition क्लास definition के बाहर होनी ही चाहिए, आमतौर पर क्लास की implementation फ़ाइल में initialize किया जाता है।

क्लास में declare करें:

static int ap;

क्लास implementation फ़ाइल में उपयोग करें:

int Apple::ap=666

इस मामले में, c++11 में declare करते ही initialize नहीं किया जा सकता, यानी ऊपर वाला उपयोग तरीका।

संबंधित विषय

टिप्पणियाँ