Associated container
Article 24 when it comes to efficiency, it should be carefully selected between map::operator [] and map insert
New element
class Widget { public: Widget() { std::cout << "Widget::Widget()" << std::endl; } Widget(double val) { _val = val; std::cout << "Widget::Widget(double)" << std::endl; } Widget& operator=(double val) { std::cout << "Widget::operator=()" << std::endl; _val = val; } double _val; }; int main() { std::map<int, Widget> widgetMap; widgetMap[1] = 1.5; std::cout << "---------" << std::endl; widgetMap.insert(std::map<int, Widget>::value_type(2, 1.5)); }
The result is
Widget::Widget() Widget::operator=() --------- Widget::Widget(double)
analysis:
widgetMap[1] = 1.5 widgetMap[1]yes widgetMap.operator[](1)Simplified, operator[]Returns a Widget Object, at the beginning map There are no elements in, so the default constructor will be called first to construct a temporary object Widget,Then call Widget The assignment operator of widgetMap[1] = 1.5 Actually, it's equivalent to Widget tmp; std::pair<std::map<int, Widget>::iterator, bool> cit = widgetMap.insert(std::map<int, Widget>::value_type(1, tmp)); cit.first->second = 1.5;
Update element:
class Widget { public: Widget() { std::cout << "Widget::Widget()" << std::endl; } Widget(double val) { _val = val; std::cout << "Widget::Widget(double)" << std::endl; } Widget(const Widget& widget) { _val = widget._val; std::cout << "Widget::Widget(const Widget& widget)" << std::endl; } Widget& operator=(double val) { std::cout << "Widget::operator=()" << std::endl; _val = val; } double _val; }; int main() { std::map<int, Widget> widgetMap; widgetMap[1] = 1.5; std::cout << "---------" << std::endl; widgetMap.insert(std::map<int, Widget>::value_type(1, 1.5)); }
When updating the element, insert operator [] constructs a temporary object first, and then inserts the object through the copy constructor.
The operator [] only needs to assign a value to the existing object in the map. Therefore, the operator [] is more efficient.
A general method of adding elements is written in this paper
template<typename MapType, typename KeyArgType, typename ValueArgtype> typename MapType::iterator efficientAddOrUpdate(MapType& m, const KeyArgType& k, const ValueArgtype& v) { typename MapType::iterator Ib = m.lower_bound(k); if(Ib != m.end() && !(m.key_comp()(k, Ib->first))) { Ib->second = v; return Ib; } else { typedef typename MapType::value_type MVT; return m.insert(Ib, MVT(k, v)); } } /* * An interesting thing here is to use the template types KeyArgType and ValueArgtype instead of MapType: key_type And MapType::mapped_type. template<typename MapType> typename MapType::iterator efficientAddOrUpdate(MapType& m, MapType::key_type k, MapType::mapped_type v) The reason is that maptype:: key is used_ Type may force unnecessary type conversions For example: class Widget { public: Widget& operator=(double weight); }; std::map<int, Widget> m; efficientAddOrUpdate(m, 10, 1.5); It is assumed that the element is updated without MapType::key_type can be directly assigned through widget & operator = (double weight); But use MapType::key_type, 1.5 needs to be constructed into a Widget object and then assigned, which increases the overhead of temporary object construction and deconstruction **/
Summary:
insert for new elements and operator [] for updated elements
Clause 25: familiarity with non-standard hash containers
This regulation is out of date. The hash container unordered was provided after C++11_ set,unordered_map, etc
iterator
Clause 26: try to replace const with iterator_ iterator,reverse_iterator and
const_reverse_iterator
This regulation is out of date at present. This article is due to the early recognition of const_ There are many restrictions on the use of iterator. Container insert and erase do not accept const_iterator type; In addition, iterator and Const_ When iterators are mixed, their operations such as comparison, addition and subtraction cannot even be compiled. Therefore, it is recommended to use iterator after comprehensive consideration
Clause 27: convert const with distance and advance_ Convert iterator to iterator
The iterator can be implicitly converted to const_iterator, but const_ An iterator cannot be converted to an iterator, even through const_ Neither can cast, iterator and const_iterator is not T and const T in the usual sense. They are two completely different classes.
The correct way is: STD:: advance (iterator, STD:: distance < const_iterator > (iterator, const_iterator));
std::advance(iterator, n) is to move the iterator forward or backward n positions
std::distance(first, last) is the offset between the iterator first and the iterator last
typedef std::deque<int> IntDeque; typedef IntDeque::iterator Iter; typedef IntDeque::const_iterator ConstIter; int main() { IntDeque deque{ 1, 2 }; Iter it = deque.begin(); ConstIter cit = --deque.cend(); // Point const iterator to the last element std::advance(it, std::distance<ConstIter>(it, cit)); // Point the iterator to the same location as const iterator std::cout << *it << std::endl; }
be careful:
std::advance(iterator, std::distance<const_iterator>(iterator, const_iterator)); < const here_ Iterator > must be specified.
From the implementation of distance below, we can see that both input parameters need to be of the same type, while ours are iterator and const_ There are two different types of iterator, which the compiler cannot recognize when deriving the InputIterator type.
template<typename InputIterator> typename iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last);
Clause 28: learn how to use reverse_ The base of the iterator gets the iterator
https://en.cppreference.com/w/cpp/iterator/reverse_iterator/base
The insert and erase operations of the container do not support reverse_iterator, such as vector, so when you have a reverse_ The iterator needs to be converted into an iterator through the base function before completing the work
Insert:
reverse_iterator.base() and reverse_iterator is equivalent
vector<int> v{1, 2, 3, 4, 5}; vector<int>::reverse_iterator ri = v.rbegin(); v.insert(ri.base());
Delete:
reverse_ The previous element of iterator. Base() is and reverse_iterator equivalence
vector<int> v{1, 2, 3, 4, 5}; vector<int>::reverse_iterator ri = v.rbegin(); // v.erase(--ri.base()); Compile but?? v.erase((++ri).base());
Clause 29: consider using istreambuf when one character input is required_ iterator
std::ifstream inputFile("interestingData.txt"); inputFile.unset(ios::skipws); // Turn off the ignore space flag of inputFile std::string fileData((std::istream_iterator<char>(inputFile)), std::istream_iterator<char>());
std::ifstream inputFile("interestingData.txt"); std::string fileData((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>());
Differences between the two:
1,istream_ The iterator uses the operator > > function for real reading, while the operator > > function ignores spaces by default. Therefore, if you want to keep spaces, clear the skipws flag of the input stream; And istreambuf_ The iterator does not ignore any characters; it istreambuf_ The iterator object enters the buffer of the stream and reads the next character directly. (more specifically, an istreambuf_iterator object reading from an istream s will call s.rdbuf() - > sgetc() to read the next character of S.)
2,istreambuf_iterator is better than istream_iterators are more efficient because istream_iterators rely on the operator > > function to format the input
Summary: for unformatted character by character input, istreambuf should always be considered_ iterato; For unformatted output, you can also consider using ostrambuf_ Iterator replaces ostream_iterator
todo: look at istream_iterator and istreambuf_ The implementation of iterator to see how to format input
algorithm
Clause 30: ensure that the target range is large enough