STL चरित्र लक्षण के बिंदु क्या है?


84

मुझे लगता है कि SGI STL संदर्भ की मेरी प्रति में, चरित्र लक्षण के बारे में एक पृष्ठ है, लेकिन मैं नहीं देख सकता कि ये कैसे उपयोग किए जाते हैं? क्या वे string.h फ़ंक्शन को प्रतिस्थापित करते हैं? वे इसका उपयोग नहीं करते हैं std::string, उदाहरण के लिए length()पर विधि std::stringविशेषता लक्षण length()विधि का उपयोग नहीं करता है । चरित्र लक्षण क्यों मौजूद हैं और क्या वे कभी अभ्यास में उपयोग किए जाते हैं?

जवाबों:


172

चरित्र लक्षण धाराओं और तारों के पुस्तकालयों का एक अत्यंत महत्वपूर्ण घटक है क्योंकि वे धारा / स्ट्रिंग वर्गों को उन वर्णों के तर्क को अलग करने की अनुमति देते हैं जो उन वर्णों के तर्क से संग्रहीत किए जा रहे हैं कि उन वर्णों पर क्या हेरफेर किया जाना चाहिए।

के साथ शुरू करने के लिए, डिफ़ॉल्ट चरित्र लक्षण वर्ग, char_traits<T>C ++ मानक में बड़े पैमाने पर उपयोग किया जाता है। उदाहरण के लिए, कोई वर्ग नहीं कहा जाता है std::string। बल्कि, एक वर्ग टेम्पलेट std::basic_stringहै जो इस तरह दिखता है:

template <typename charT, typename traits = char_traits<charT> >
    class basic_string;

फिर, std::stringके रूप में परिभाषित किया गया है

typedef basic_string<char> string;

इसी तरह, मानक धाराओं को परिभाषित किया गया है

template <typename charT, typename traits = char_traits<charT> >
    class basic_istream;

typedef basic_istream<char> istream;

तो क्यों इन वर्गों को संरचित किया जाता है जैसे वे हैं? हमें टेम्पलेट तर्क के रूप में एक अजीब लक्षण वर्ग का उपयोग क्यों करना चाहिए?

इसका कारण यह है कि कुछ मामलों में हम एक स्ट्रिंग की तरह चाहते हो सकते हैं std::string, लेकिन कुछ अलग गुणों के साथ। इसका एक क्लासिक उदाहरण यह है कि यदि आप केस को अनदेखा करना चाहते हैं तो स्ट्रिंग्स को स्टोर करना चाहते हैं। उदाहरण के लिए, मैं एक स्ट्रिंग बनाना चाहता हूं, जिसे CaseInsensitiveStringमैं कह सकता हूं

CaseInsensitiveString c1 = "HI!", c2 = "hi!";
if (c1 == c2) {  // Always true
    cout << "Strings are equal." << endl;
}

यही है, मेरे पास एक स्ट्रिंग हो सकती है जहां केवल उनके मामले की संवेदनशीलता में भिन्न होने वाले दो तारों की तुलना बराबर होती है।

अब, मान लीजिए कि मानक पुस्तकालय लेखकों ने लक्षणों का उपयोग किए बिना तार डिजाइन किए हैं। इसका मतलब यह होगा कि मुझे मानक पुस्तकालय में एक बेहद शक्तिशाली स्ट्रिंग क्लास होगी जो मेरी स्थिति में पूरी तरह से बेकार थी। मैं इस स्ट्रिंग क्लास के लिए ज्यादा कोड का पुन: उपयोग नहीं कर सकता था, क्योंकि तुलना करने वाले हमेशा काम करेंगे कि मैं उन्हें कैसे काम करना चाहता था। लेकिन लक्षणों का उपयोग करके, वास्तव में उस कोड का पुन: उपयोग करना संभव है std::stringजो केस-असंवेदनशील स्ट्रिंग प्राप्त करने के लिए ड्राइव करता है।

यदि आप C ++ ISO मानक की एक प्रति खींचते हैं और स्ट्रिंग के तुलना संचालकों के काम करने की परिभाषा पर गौर करते हैं, तो आप देखेंगे कि वे सभी compareफ़ंक्शन के संदर्भ में परिभाषित हैं । यह फ़ंक्शन कॉल करके परिभाषित किया गया है

traits::compare(this->data(), str.data(), rlen)

strवह स्ट्रिंग कहां है जिसकी आप तुलना कर रहे हैं और rlenदो स्ट्रिंग लंबाई से छोटी है। यह वास्तव में काफी दिलचस्प है, क्योंकि इसका मतलब है कि टेम्पलेट पैरामीटर के रूप में निर्दिष्ट लक्षण प्रकार द्वारा निर्यात किए गए फ़ंक्शन compareका उपयोग सीधे करता है compare! नतीजतन, अगर हम एक नए लक्षण वर्ग को परिभाषित करते हैं, तो compareइसे परिभाषित करते हैं ताकि यह पात्रों के मामले की तुलना में असंवेदनशील हो, हम एक स्ट्रिंग वर्ग का निर्माण कर सकते हैं जो व्यवहार की तरह है std::string, लेकिन चीजों को असंवेदनशील व्यवहार करता है!

यहाँ एक उदाहरण है। हम std::char_traits<char>उन सभी कार्यों के लिए डिफ़ॉल्ट व्यवहार प्राप्त करते हैं जो हम नहीं लिखते हैं:

class CaseInsensitiveTraits: public std::char_traits<char> {
public:
    static bool lt (char one, char two) {
        return std::tolower(one) < std::tolower(two);
    }

    static bool eq (char one, char two) {
        return std::tolower(one) == std::tolower(two);
    }

    static int compare (const char* one, const char* two, size_t length) {
        for (size_t i = 0; i < length; ++i) {
            if (lt(one[i], two[i])) return -1;
            if (lt(two[i], one[i])) return +1;
        }
        return 0;
    }
};

(नोटिस मैंने भी परिभाषित किया है eqऔर ltयहाँ है, जो क्रमशः समानता और कम-से-कम के लिए वर्णों की तुलना करते हैं, और फिर compareइस फ़ंक्शन के संदर्भ में परिभाषित किए जाते हैं)।

अब जब हमारे पास यह विशेषता वर्ग है, तो हम CaseInsensitiveStringतुच्छ रूप से परिभाषित कर सकते हैं

typedef std::basic_string<char, CaseInsensitiveTraits> CaseInsensitiveString;

और वोइला! अब हमारे पास एक स्ट्रिंग है जो हर मामले को असंवेदनशील तरीके से व्यवहार करता है!

बेशक, लक्षण का उपयोग करने के लिए इसके अलावा अन्य कारण भी हैं। उदाहरण के लिए, यदि आप एक स्ट्रिंग को परिभाषित करना चाहते हैं जो निश्चित आकार के कुछ अंतर्निहित चरित्र प्रकार का उपयोग करता है, तो आप char_traitsउस प्रकार पर विशेषज्ञ कर सकते हैं और फिर उस प्रकार से तार बना सकते हैं। उदाहरण के लिए, Windows API में, एक प्रकार TCHARहै जो या तो एक संकीर्ण या विस्तृत वर्ण है जो इस बात पर निर्भर करता है कि आपने मैक्रोप्रोसेसिंग के दौरान क्या सेट किया है। फिर आप TCHARलेखन से तार को बाहर कर सकते हैं

typedef basic_string<TCHAR> tstring;

और अब आपके पास एक तार है TCHAR

इन सभी उदाहरणों में, ध्यान दें कि हमने बस कुछ टेम्प्लेट क्लास को परिभाषित किया था (या पहले से मौजूद एक का उपयोग किया था) उस प्रकार के लिए एक स्ट्रिंग प्राप्त करने के लिए कुछ टेम्पलेट प्रकार के पैरामीटर के रूप में। इसका पूरा बिंदु यह है कि basic_stringलेखक को केवल यह निर्दिष्ट करने की आवश्यकता है कि कैसे लक्षणों का उपयोग किया जाए और हम जादुई तरीके से उन्हें तार पाने के लिए डिफ़ॉल्ट के बजाय हमारे लक्षणों का उपयोग कर सकते हैं, जिनमें कुछ अति सूक्ष्म अंतर या क्वर्की डिफ़ॉल्ट स्ट्रिंग प्रकार का हिस्सा नहीं है।

उम्मीद है की यह मदद करेगा!

EDIT : जैसा कि @phooji ने बताया, लक्षण की यह धारणा न केवल STL द्वारा उपयोग की जाती है, न ही यह C ++ के लिए विशिष्ट है। पूरी तरह से बेशर्म आत्म-प्रचार के रूप में, कुछ समय पहले मैंने एक टर्नरी खोज ट्री ( यहां वर्णित एक प्रकार का मूलांक वृक्ष ) का कार्यान्वयन लिखा था, जो किसी भी प्रकार के तारों को संग्रहीत करने के लिए लक्षणों का उपयोग करता है और ग्राहक जो भी तुलना करना चाहता है, उनका उपयोग करता है। यह एक दिलचस्प रीड हो सकता है यदि आप एक उदाहरण देखना चाहते हैं कि यह व्यवहार में कहां उपयोग किया जाता है।

संपादित करें : आपके दावे के जवाब में , जो std::stringउपयोग नहीं करता है traits::length, यह पता चलता है कि यह कुछ स्थानों पर करता है। सबसे विशेष रूप से, जब आप सी-स्टाइल स्ट्रिंग से std::stringबाहर का निर्माण करते हैं, तो char*स्ट्रिंग की नई लंबाई traits::lengthउस स्ट्रिंग पर कॉल करके प्राप्त की जाती है । ऐसा लगता है कि traits::lengthइसका उपयोग ज्यादातर वर्णों के सी-स्टाइल अनुक्रमों से निपटने के लिए किया जाता है, जो कि सी ++ में स्ट्रिंग्स के "कम से कम सामान्य भाजक" हैं, जबकि std::stringइसका उपयोग मनमाना सामग्री के तार के साथ काम करने के लिए किया जाता है।


14
ऐसा लगता है कि आपने अपने उपयोगकर्ता नाम के साथ न्याय किया है :) शायद यह भी प्रासंगिक है: कई बढ़ावा पुस्तकालयों अवधारणाओं और प्रकार की विशेषता वर्गों का उपयोग करते हैं, इसलिए यह सिर्फ मानक पुस्तकालय नहीं है। इसके अलावा, इसी तरह की तकनीकों का उपयोग अन्य भाषाओं में टेम्प्लेट के उपयोग के बिना किया जाता है, गूढ़ उदाहरण देखें: ocaml.janestreet.com/?q=node/11
फ़ूजी

2
अच्छी संरचना (टेरनेरी सर्च ट्री), हालाँकि मैं बताता हूँ कि ट्राई को विभिन्न तरीकों से "संकुचित" किया जा सकता है: एकल वर्णों के बजाय एक बच्चे को इंगित करने के लिए पात्रों की श्रेणी / 1 का उपयोग करना (लाभ स्पष्ट है), 2 / पथ संपीड़न (पेट्रीसिया पेड़) और शाखाओं के अंत में 3 / बाल्टी (यानी, जब तक कि के से कम न हो, तार के एक क्रमबद्ध सरणी का उपयोग करें)। उन लोगों को मिलाकर (मैंने 1 और 3 को मिलाकर) एक स्थिर कारक से अधिक गति प्रदर्शन को प्रभावित किए बिना स्मृति की खपत को काफी कम कर दिया (और वास्तव में, बाल्टियों की संख्या में कमी आती है)।
मैथ्यू एम।

2
@ dan04: अपने फ़ंक्शन का उपयोग करने के लिए किसी भी मानक वर्ग / एल्गोरिदम को प्राप्त करने का प्रयास करें।
Xio

2
इसलिए ... इसे संक्षेप में कहें, तो लक्षण बस कुछ प्रकार के इंटरफ़ेस का उपयोग करते हैं, जो बुनियादी_स्टृंग वर्ग द्वारा विभिन्न प्रकार के पात्रों में हेरफेर करने के लिए उपयोग किए जाते हैं, भले ही वे वास्तव में क्या हैं?
विरू .721

1
@ Virus721 लक्षण एक दिए गए वर्ग में "प्लग इन" के बजाय कार्यान्वयन नहीं हैं? एक गुण से एक वर्ग क्या प्राप्त करता है, IMHO, एक कार्यान्वयन, एक इंटरफ़ेस नहीं। लक्षण का अपना इंटरफ़ेस है, ज़ाहिर है।
dom_beau
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.