C ++ में वैश्विक स्थिरांक को परिभाषित करना


81

मैं कई स्रोत फ़ाइलों में दिखाई देने के लिए C ++ में एक निरंतर परिभाषित करना चाहता हूं। मैं हेडर फ़ाइल में इसे परिभाषित करने के निम्नलिखित तरीकों की कल्पना कर सकता हूं:

  1. #define GLOBAL_CONST_VAR 0xFF
  2. int GLOBAL_CONST_VAR = 0xFF;
  3. कुछ फ़ंक्शन मान को पुन: प्राप्त कर रहा है (उदा int get_GLOBAL_CONST_VAR())
  4. enum { GLOBAL_CONST_VAR = 0xFF; }
  5. const int GLOBAL_CONST_VAR = 0xFF;
  6. extern const int GLOBAL_CONST_VAR; और एक स्रोत फ़ाइल में const int GLOBAL_CONST_VAR = 0xFF;

विकल्प (1) - निश्चित रूप से वह विकल्प नहीं है जिसका आप उपयोग करना चाहते हैं

विकल्प (2) - हेडर फ़ाइल का उपयोग करके प्रत्येक ऑब्जेक्ट फ़ाइल में चर के उदाहरण को परिभाषित करना

विकल्प (3) - ज्यादातर मामलों में IMO की हत्या हो गई है

विकल्प (4) - कई मामलों में शायद अच्छा नहीं है क्योंकि एनम का कोई ठोस प्रकार नहीं है (C ++ 0X प्रकार को परिभाषित करने की संभावना जोड़ देगा)

इसलिए ज्यादातर मामलों में मुझे (5) और (6) के बीच चयन करना होगा। मेरे सवाल:

  1. आप क्या पसंद करते हैं (5) या (6)?
  2. क्यों (5) ठीक है, जबकि (2) नहीं है?

1
5 बनाम 2: "कॉन्स्ट" का अर्थ है आंतरिक संबंध। जब आप इस संस्करण -5 शीर्षक को कई अनुवाद इकाइयों में शामिल करते हैं तो आप "एक परिभाषा नियम" का उल्लंघन नहीं करेंगे। इसके अलावा, कॉन्सलर कंपाइलर को "निरंतर फोल्डिंग" करने की अनुमति देता है जबकि नॉन-कॉस्ट वैरिएबल का मूल्य बदल सकता है। विकल्प 6 गलत है। बाहरी लिंक करने के लिए आपको cpp फ़ाइल में "बाहरी" की आवश्यकता है, अन्यथा आपको लिंकर की त्रुटियां मिलेंगी। विकल्प 6 में मूल्य को छिपाने का लाभ है। लेकिन यह भी निरंतर तह असंभव बनाता है।
बेच दें

जवाबों:


32

(५) ठीक वही कहता है जो आप कहना चाहते हैं। साथ ही यह कंपाइलर को ज्यादातर समय दूर रहने देता है। (6) दूसरी ओर संकलक को कभी भी इसे दूर करने की अनुमति नहीं देगा क्योंकि संकलक को पता नहीं है कि आप इसे अंततः बदलेंगे या नहीं।


1
OTOH, 5 ODR के उल्लंघन के रूप में तकनीकी रूप से अवैध है। हालांकि अधिकांश कंपाइलर इसे अनदेखा करेंगे।
जोएल

एह, मैं इसे कुछ भी परिभाषित नहीं करने के बारे में सोचना पसंद करता हूं, मैंने संकलक को एक नंबर को एक सुंदर नाम देने के लिए कहा। सभी इरादों और उद्देश्यों के लिए यही (5) है, जिसका मतलब रनटाइम के दौरान ओवरहेड नहीं है।
ब्लाइंड

2
क्या (5) ODR का उल्लंघन है? यदि यह है, तो (6) बेहतर है। संकलक (6) के मामले में "आपको नहीं पता होगा कि आप इसे कैसे बदलेंगे" ? extern const int ...और const int ...क्या दोनों स्थिर हैं?
21

4
AFAIK, 5) और 6) के बीच, केवल 6) को अनुमति दी जाती है जब स्थिरांक का प्रकार अंतर-आधारित नहीं होता है।
Kलेम

11
कोई ODR उल्लंघन नहीं है, लगातार ऑब्जेक्ट डिफ़ॉल्ट रूप से स्थिर होते हैं।
अवाकर

71

निश्चित रूप से विकल्प 5 के साथ जाएं - यह सुरक्षित प्रकार है और कंपाइलर को ऑप्टिमाइज़ करने की अनुमति देता है (उस चर का पता न लें :) इसके अलावा अगर यह हेडर में है - तो इसे ग्लोबल स्कोप को प्रदूषित करने से बचाने के लिए किसी नाम स्थान पर चिपका दें:

// header.hpp
namespace constants
{
    const int GLOBAL_CONST_VAR = 0xFF;
    // ... other related constants

} // namespace constants

// source.cpp - use it
#include <header.hpp>
int value = constants::GLOBAL_CONST_VAR;

3
जब मैं header.hppकई स्रोत फ़ाइलों में शामिल करने का प्रयास करता हूं तो मुझे पुनर्परिभाषित त्रुटि मिलती है ।
LRDPRDX

यह निश्चित नहीं है कि यह अभी भी क्यों उखाड़ा गया है - यह लगभग दस साल हो गया है, लेकिन अब हमारे पास इस constexprतरह की चीजों के लिए गणनाएं हैं और टाइप किए गए हैं।
निकोलाई Fetissov

24

(5) (6) की तुलना में "बेहतर" है क्योंकि यह GLOBAL_CONST_VARसभी अनुवाद इकाइयों में इंटीग्रल कॉन्स्टेंट एक्सप्रेशन (ICE) के रूप में परिभाषित होता है । उदाहरण के लिए, आप इसका उपयोग सरणी आकार और सभी अनुवाद इकाइयों में केस लेबल के रूप में कर सकेंगे। (6) के मामले मेंGLOBAL_CONST_VAR केवल उस अनुवाद इकाई में एक ICE होगा जहां इसे परिभाषित किया गया है और केवल परिभाषा के बिंदु के बाद। अन्य अनुवाद इकाइयों में यह ICE की तरह काम नहीं करेगा।

हालांकि, ध्यान रखें कि (5) GLOBAL_CONST_VARआंतरिक संबंध देता है , जिसका अर्थ है कि GLOBAL_CONST_VARप्रत्येक अनुवाद इकाई में "पता पहचान" अलग होगी, अर्थात&GLOBAL_CONST_VAR आपको एक अलग सूचक मान देगा। अधिकांश उपयोग के मामलों में यह कोई मायने नहीं रखता है, लेकिन अगर आपको एक निरंतर ऑब्जेक्ट की आवश्यकता होगी, जिसमें लगातार वैश्विक "पता पहचान" हो, तो आपको (6) के साथ जाना होगा, जिसमें निरंतर के ICE-nessificing बलिदान प्रक्रिया।

इसके अलावा, जब स्थिर का ICE-n एक मुद्दा नहीं है (एक अभिन्न प्रकार नहीं) और प्रकार का आकार बड़ा हो जाता है (स्केलर प्रकार नहीं), तो (6) आमतौर पर (5) की तुलना में बेहतर दृष्टिकोण बन जाता है।

(2) ठीक नहीं है क्योंकि GLOBAL_CONST_VAR(2) में डिफ़ॉल्ट रूप से बाहरी लिंकेज है। यदि आप इसे हेडर फ़ाइल में रखते हैं, तो आप आमतौर पर की कई परिभाषाओं के साथ समाप्त हो जाएंगे GLOBAL_CONST_VAR, जो कि एक त्रुटि है। constC ++ में ऑब्जेक्ट्स में डिफ़ॉल्ट रूप से आंतरिक जुड़ाव होता है, यही वजह है कि (5) काम करता है (और यही कारण है कि, जैसा कि मैंने ऊपर कहा है, आपको GLOBAL_CONST_VARप्रत्येक अनुवाद इकाई में एक अलग, स्वतंत्र मिलता है )।


C ++ 17 से शुरू करके आपके पास घोषणा करने का एक विकल्प है

inline extern const int GLOBAL_CONST_VAR = 0xFF;

एक हेडर फ़ाइल में। यह आपको सभी अनुवाद इकाइयों में एक ICE देता है (ठीक उसी तरह विधि (5)) जो वैश्विक पते की पहचान को बनाए रखता है GLOBAL_CONST_VAR- सभी अनुवाद इकाइयों में इसका समान पता होगा।


8

यदि आप C ++ 11 या उसके बाद का उपयोग करते हैं, तो संकलन-समय स्थिरांक का उपयोग करने का प्रयास करें:

constexpr int GLOBAL_CONST_VAR{ 0xff };

1
IMHO, यह इस समस्या का एकमात्र संतोषजनक समाधान है।
15

5

यदि यह एक स्थिरांक होने जा रहा है, तो आपको इसे एक स्थिरांक के रूप में चिह्नित करना चाहिए - यही कारण है कि 2 मेरी राय में खराब है।

संकलक मूल्य के कास्ट प्रकृति का उपयोग करके कुछ गणित का विस्तार कर सकता है, और वास्तव में अन्य ऑपरेशन जो मूल्य का उपयोग करते हैं।

5 और 6 के बीच चुनाव - हम्म; 5 बस मुझे बेहतर लगता है।

6 में) यह घोषणा से मूल्य अनावश्यक रूप से अलग है।

मेरे पास आमतौर पर इनमें से एक या अधिक हेडर होते हैं जो केवल उनके भीतर स्थिरांक आदि को परिभाषित करते हैं, और फिर कोई अन्य 'चतुर' सामान - अच्छा हल्का हेडर जो आसानी से कहीं भी शामिल किया जा सकता है।


3
(6) अनावश्यक टुकड़ी नहीं है, यह एक जानबूझकर पसंद है। यदि आपके पास बहुत सारे स्थिरांक हैं जो बड़े हैं, तो आप निष्पादन योग्य में बहुत जगह बर्बाद करते हैं यदि आप उन्हें (6) के रूप में घोषित नहीं करते हैं। गणित पुस्तकालयों में हो सकता है ... अपशिष्ट 100k के नीचे हो सकता है लेकिन यहां तक ​​कि कभी-कभी महत्वपूर्ण है। (कुछ संकलक के पास इसके चारों ओर पाने के अन्य तरीके हैं, मुझे लगता है कि MSVC में "एक बार" विशेषता है, या कुछ इसी तरह की है।)
दान ओल्सन

(5) के साथ आप वास्तव में यह सुनिश्चित नहीं कर सकते कि यह स्थिर रहेगा (आप हमेशा कब्ज दूर कर सकते हैं)। यही कारण है कि मैं अभी भी enum प्रकार पसंद करेंगे।
fmuecke

@ डैन ओल्सन - यह एक बहुत अच्छा बिंदु है - मेरा जवाब इस तथ्य पर आधारित था कि यहां शामिल प्रकार एक इंट है; लेकिन बड़े मूल्यों के साथ काम करते समय बाहरी घोषणा वास्तव में एक बेहतर योजना है।
आंद्रास ज़ोल्टन

@fmuecke - हां, आप सही हैं - जिस स्थिति में एनम मान इसे रोकती है। लेकिन क्या इसका मतलब है कि हमें हमेशा अपने मूल्यों को इस तरह से लिखने से बचाना चाहिए? यदि कोई प्रोग्रामर कोड का दुरुपयोग करना चाहता है, तो बहुत सारे क्षेत्र हैं जहां एक (target_type *) ((शून्य *) & value) कास्ट कहर बरपा सकता है, कि हम पकड़ नहीं सकते हैं, कभी-कभी हमें बस उन पर अपना भरोसा रखना होगा; और वास्तव में खुद, नहीं?
एंड्रास ज़ोल्टन

@fmuecke एक वैरिएबल जिसे कांस्टिट घोषित किया गया है उसे प्रोग्राम द्वारा बदला नहीं जा सकता है (ऐसा करने का प्रयास अपरिभाषित व्यवहार है)। const_cast केवल उन स्थितियों में परिभाषित किया गया है जहां मूल चर को const (उदाहरण के लिए, एक कॉन्स्ट में कॉन्स्टेबल के रूप में एक नॉन-कॉस्ट वैल्यू पास करना) घोषित नहीं किया गया था।
डेविड स्टोन

5

अपने दूसरे प्रश्न का उत्तर देने के लिए:

(२) गैरकानूनी है क्योंकि यह वन डेफिनिशन नियम का उल्लंघन करता है। यह GLOBAL_CONST_VARहर फाइल को परिभाषित करता है जहां यह शामिल है, यानी एक से अधिक बार। (5) कानूनी है क्योंकि यह एक परिभाषा नियम के अधीन नहीं है। प्रत्येक GLOBAL_CONST_VARएक अलग परिभाषा है, उस फ़ाइल के लिए स्थानीय जहाँ यह शामिल है। वे सभी परिभाषाएँ समान नाम और मूल्य को साझा करती हैं, लेकिन उनके पते भिन्न हो सकते हैं।


4

सी ++ 17 inlineचर

यह भयानक C ++ 17 फीचर हमें इसकी अनुमति देता है:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

संकलित करें और चलाएं:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

गिटहब ऊपर

यह भी देखें: इनलाइन चर कैसे काम करते हैं?

इनलाइन चरों पर C ++ मानक

C ++ मानक गारंटी देता है कि पते समान होंगे। C ++ 17 N4659 मानक ड्राफ्ट 10.1.6 "इनलाइन विनिर्देशक":

6 बाहरी लिंक के साथ एक इनलाइन फ़ंक्शन या चर सभी अनुवाद इकाइयों में एक ही पता होगा।

cppreference https://en.cppreference.com/w/cpp/language/inline बताता है कि अगर staticनहीं दिया गया है, तो इसका बाहरी संबंध है।

इनलाइन चर कार्यान्वयन

हम यह देख सकते हैं कि इसे किस प्रकार लागू किया जाता है:

nm main.o notmain.o

जिसमें है:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

और man nmइसके बारे में कहते हैं u:

"यू" प्रतीक एक अद्वितीय वैश्विक प्रतीक है। यह ईएलएफ प्रतीक बाइंडिंग के मानक सेट के लिए एक जीएनयू विस्तार है। ऐसे प्रतीक के लिए गतिशील लिंकर यह सुनिश्चित करेगा कि पूरी प्रक्रिया में इस नाम के साथ सिर्फ एक प्रतीक है और उपयोग में है।

इसलिए हम देखते हैं कि इसके लिए एक समर्पित ईएलएफ एक्सटेंशन है।

GCC 7.4.0, उबंटू 18.04 पर परीक्षण किया गया।


2
const int GLOBAL_CONST_VAR = 0xFF;

क्योंकि यह एक निरंतर है!


1
और इसे मैक्रो की तरह नहीं माना जाएगा, जिससे डीबग करना आसान हो जाएगा।
kayleeFrye_onDeck

-1, यह कई स्रोत फ़ाइलों में हेडर को शामिल करते समय पुनर्निर्देशन चेतावनी / त्रुटियों को जन्म देगा। इसके अलावा यह जवाब निकोलाई फेटिसोव के जवाब का एक डुप्लिकेट है।
lanoxx

1

यह आपकी आवश्यकताओं पर निर्भर करता है। (5) सबसे सामान्य उपयोग के लिए सबसे अच्छा है, लेकिन अक्सर हर वस्तु फ़ाइल में लगातार भंडारण स्थान को ऊपर ले जाता है। (६) इसके आसपास उन स्थितियों में पहुँच सकते हैं जहाँ यह महत्वपूर्ण है।

(4) भी एक सभ्य विकल्प है यदि आपकी प्राथमिकता यह गारंटी दे रही है कि भंडारण स्थान कभी आवंटित नहीं किया गया है, लेकिन यह केवल अभिन्न स्थिरांक के लिए काम करता है।


1
#define GLOBAL_CONST_VAR 0xFF // this is C code not C++
int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled
Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision
enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration)
const int GLOBAL_CONST_VAR = 0xFF; // it is the best
extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.