हम std :: वेक्टर के डेटा पॉइंटर को क्यों नहीं बदल सकते?
मेरे पास एक सरणी char* source
और एक वेक्टर है std::vector<char> target
। मैं डेटा को कॉपी किए बिना O (1) में वेक्टर target
बिंदु बनाना चाहता हूं source
।
इन पंक्तियों के साथ कुछ:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
जहां वेक्टर की ओर इशारा किया जाता है वहां मैन्युअल रूप से परिवर्तन करना संभव क्यों नहीं है? क्या कुछ विशिष्ट, असहनीय समस्या है जो यह संभव होती तो उत्पन्न होती?
जवाब
C ++ vector
क्लास मेमोरी में लगातार गारंटी के साथ तत्वों को जोड़ने और हटाने का समर्थन करता है। यदि आप vector
मौजूदा मेमोरी बफ़र के साथ अपना इनिशियलाइज़ कर सकते हैं , और इसमें पर्याप्त तत्व जोड़ सकते हैं, तो यह या तो ओवरफ्लो करेगा या रीएलोकेशन की आवश्यकता होगी।
यह vector
मानता है कि यह अपने आंतरिक बफर का प्रबंधन करता है , अर्थात, यह आवंटित कर सकता है, निपटा सकता है, जब चाहे तब इसे आकार दे सकता है (निश्चित रूप से, निश्चित रूप से)। यदि आपको ऐसी चीज़ की आवश्यकता है जो इसके बफर को प्रबंधित करने की अनुमति नहीं है , तो आप इसका उपयोग नहीं कर सकते हैं vector
- एक अलग डेटा संरचना का उपयोग करें या एक स्वयं लिखें।
आप vector
अपने डेटा को कॉपी करके (दो पॉइंटर्स के साथ एक कंस्ट्रक्टर का उपयोग करके) एक ऑब्जेक्ट बना सकते हैं assign
, लेकिन यह स्पष्ट रूप से वह नहीं है जो आप चाहते हैं।
वैकल्पिक रूप से, आप उपयोग कर सकते हैं string_view, जो लगभग या शायद बिल्कुल वही दिखता है जिसकी आपको आवश्यकता है।
std::vector
को अंतर्निहित बफर का मालिक माना जाता है। आप बफ़र को बदल सकते हैं लेकिन यह परिवर्तन आबंटन का कारण बनता है अर्थात स्रोत बफ़र की एक प्रतिलिपि बनाता है जो आप नहीं चाहते (जैसा कि प्रश्न में कहा गया है)।
आप निम्नलिखित कर सकते हैं:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
लेकिन फिर से std::vector::assign:
श्रेणी [प्रथम, अंतिम) में उन लोगों की प्रतियों के साथ सामग्री को प्रतिस्थापित करता है ।
तो कॉपी फिर से किया जाता है। उपयोग करते समय आप इससे दूर नहीं हो सकते std::vector
।
यदि आप डेटा को कॉपी नहीं करना चाहते हैं, तो आपको std::span
C ++ 20 (या अपना खुद का स्पैन बनाना) या उपयोग करना चाहिए std::string_view
(जो आपके लिए उपयुक्त है क्योंकि आपके पास सरणी है char
)।
पहला विकल्प: उपयोग करना std::string_view
चूँकि आप C ++ 17 तक सीमित हैं, std::string_view
आपके लिए एकदम सही हो सकता है। यह चरित्र सरणी के पहले 3 वर्णों के एक दृश्य का निर्माण करता है, जो इसके द्वारा बताए गए तत्व से शुरू होता है source
।
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
दूसरा विकल्प: std::span
C ++ 20 से उपयोग करना
std::span
C ++ 20 से आता है इसलिए यह आपके लिए सबसे सही तरीका नहीं हो सकता है, लेकिन आप इसमें रुचि रखते हैं कि यह क्या है और यह कैसे काम करता है। आप std::span
थोड़ा सामान्यीकृत संस्करण के रूप में सोच सकते हैं std::string_view
क्योंकि यह किसी भी प्रकार की वस्तुओं का एक सन्निहित अनुक्रम है, न कि केवल वर्ण। उपयोग निम्नानुसार है std::string_view
:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
तीसरा विकल्प: आपका अपना काल
यदि आप C ++ 17 तक सीमित हैं, तो आप अपनी स्वयं की span
संरचना बनाने के बारे में सोच सकते हैं । यह अभी भी एक overkill हो सकता है, लेकिन मुझे तुम्हें दिखाने के लिए (btw इस अधिक विस्तृत जवाब पर एक नज़र रखना ):
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
इसलिए उपयोग C ++ 20 के संस्करण के साथ के समान है std::span
।