Effective STL Terms 16

zhaozj2021-02-11  210

Terms 16: How to pass the data of Vector and String to the traditional API

Because the C language has been standardized in 1998, C 's backbone is trying to push the programmer from the array to the vector to VECTOR. It also obviously happens to the process of migrating the developer from the char * pointer to the String object. There is a good reason to do these transformations, including eliminating common programming errors (see Terms 13), and has the opportunity to obtain all powerful capabilities of the STL algorithm (such as see Terms 31).

However, the obstacle is still, the most common one is the existing conventional C-style API accepts array and char * pointer instead of Vector and String objects. Such API functions will still have a long time. If we want to use STL efficiently, you must share peacefully.

Fortunately, this is easy. If you have a Vector object V, you need to get a pointer to the data in V, so that it can be treated as an array, as long as & v [0] is available. For String object S, the corresponding spell is simple S.c_STR (). But it is read-only. If you understand the provisions of the ads, there must be several restrictions.

Give a given one

Vector v;

Expression V [0] produces a reference to the first element in Vector, so & v [0] is a pointer to that first element. The element in the vector is limited to the C standard for storage in continuous memory, just like an array, so if we want to pass V to this C style API:

Void Dosomething (const INT * PINTS, SIZE_T NUMINTS);

We can do this:

DOSMETHING (& V [0], v.size ());

Maybe. Probably. The only problem is that if V is empty. If this is, v.size () is 0, and & V [0] attempts to generate a pointer to something that does not exist at all. This is not a good thing. The result is not defined. One more secure method is this:

IF (! v.empty ()) {

DOSMETHING (& V [0], v.size ());

}

If you go wrong, you may encounter some people in some half-hanging people, they will tell you that you can use v.begin () instead & v [0], because (these nasty guys will tell you) Begin returns to point to the internal part of the vector The iterator, and for the vector, its iterator is actually a pointer. That is often correct, but just as the clause is said, it is not always, you should not depend here. The return type of Begin is Iterator, not a pointer, and should never use Begin when you need a pointer to the data internal data. If you decide to type v.Begin () based on some reason, you should type & * v.begin (), because this will generate the same pointer as the & v [0], so you can make you more typing opportunities and Let others feel that you can feel more embarrassed. Frankly, if you are talking to you by using V. Segin () instead of people in & v [0], you should rethink your social circle. (Translation: In VC6, if you use v.Begin () instead & v [0], the compiler will not say anything, but in the VC7 and GCC, it will trigger a compilation error)

Similar to how to point to internal data is not reliable, since String is not reliable, because the data in the string is not committed to being stored in continuous memory, (2) The internal representation of String does not commit to one NULL character ends. This explains the reason for the String member function C_STR, which returns a value of the C style design pointer to the string. So we can pass a String object S to this function, void dosomething (const char * pstring);

Like this:

DOSMETHING (S.C_STR ());

Even the length of the string is 0, it works. In that case, C_STR will return a pointer to the NULL character. Even when the string contains NULL, it works. However, if this is true, DOSMETHING is likely to interpret the first included NULL as the end of the string. String objects do not care whether end compact is accommodated, but CHAR *-based C-style API is intended.

Look at the DOSMETHING statement:

Void Dosomething (const INT * PINTS, SIZE_T NUMINTS);

Void Dosomething (const char * pstring);

In both forms, the pointer is passed to a pointer to the const. The data of Vector and String can only pass only read only without modifying its API. This is the safest thing so far. For String, this is also the only thing, because there is no promise that the pointer generated by c_str is in the internal representation of String data; it can return a pointer to a non-modified copy of the data, this copy meets the C style API pair format Requirements. (If this intimidation makes you creepy words, please feel free to do this, because it may not be found. I have not heard which liberal implementation is used.)

For Vector, there are more flexibility. If you pass V to a C-style API that modifies its element, it is no problem, but the function being called must never try to change the number of elements in the vector. For example, it must not be tried to "create" new elements on the capacity that the VECTOR has not used. If you do this, V will become inconsistent within the internal state, because it never knows your right size. v.size () will get an incorrect result. Also, if the called function is attempted to add data on a VECTOR of a size and capacity (see Terms 14), there will be catastrophic events. I don't even want to imagine it at all. It's too terrible.

You noticed that I am using the word "typical" in front of "Typical situations"? You of course noticed it. Some vectors have some additional restrictions on their data, and if you pass an API that needs to modify the Vector data, you must ensure that these additional restrictions continue to be met. For example, Terms 23 explain the sorted VECTOR is often a feasible selection to implement an associated container, but for these vectors, it is very important. If you pass a sorted vector to an API function that may modify its data, you need to pay attention to the case where the vector is no longer maintained after the return is returned.

If you want to initialize a vector with the Elements returned by the C style API, you can use the VECTOR and the array potential memory distribution compatibility to save the space of the Element of VECOTR to the API function:

// c API: This function requires a pointer to the array, and the array has the most arraysize Double Double.

// and the array is written to the data. It returns the number of Double writes, will not be greater than MaxNumdoubles

SIZE_T FILLARRAY (double * parray); Vector VD (MAXNUMDouBles); // Create a vector,

// Its size is MaxNumdouBles

vd.resize (Fillarray (& VD [0], vd.size ())); // Let Fillarray put data

// Write VD, then adjust the size of the VD

/ / Number of elements written for Fillarray

This trick can only work in Vector, because only the VECTOR is committed to having the same potential memory distribution as the array. However, if you want to initialize the String object with data from the C style API, it is also very simple. Just let the API put the data into a vector , then copy the data from the vector to String:

// c API: This function requires a pointer to an array, and the array has the most arraysize char.

// and the array is written to the data. It returns the number of CHAR writes, not greater than MaxNumChars

SIZE_T FILLSTRING (Char * PARRAY, SIZE_T ARRAYSIZE);

Vector vc (maxnumchars); // Create a vector,

// The size is MaxNumChars

Size_t charswritten = FillString (& VC [0], vc.size ()); // Let FillString write data to VC

String S (vc.begin (), vc.begin () charswritten; // from the VC through the constructor

// Copy data to s (see Terms 5)

In fact, let the C style API put the data into a vector, then copy the idea of ​​the STL container you actually want to always be effective:

SIZE_T FILLARRAY (double * parray, size_t arraysize); //

Vector VD (MaxNumdouBles); //

Vd.resize (Fillarray (& VD [0], vd.size ());

Deque d (vd.begin (), vd.end ()); // copy data to Deque

List l (vd.begin (), vd.end ()); // copy data to list

Set s (vd.begin (), vd.end ()); // Copy data to SET

In addition, this also suggests how the STL containers other than Vector and String will pass their data to the C-style API. Just copy each of the containers to Vector, then pass them to the API:

Void Dosomething (const INT * PINTS, SIZE_T NUMINTS); // C API (ibid)

SET intSet; // Save set to the API data

...

Vector v (intSet.Begin (), intSet.end ()); // Copy SET data to Vector

IF (! v.empty ()) DOSMETHING (& V [0], v.size ()); // Pass data to the API

You can also copy data into an array, then pass the array to the C style API, but why do you want to do this? Unless you know the size of the container in the compile period, you have to assign a dynamic array, and the terms 13 explains why you should always use the versions of the dynamic allocation.

转载请注明原文地址:https://www.9cbs.com/read-4788.html

New Post(0)