Effective stl reading notes

Keywords: C++

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

Posted by foid025 on Sun, 24 Oct 2021 08:32:17 -0700