Saturday, May 31, 2008

C++ #20

93. Why qsort is Still Useful in C++
C++ defines a set of generic algorithms such as sort and find. However, the corresponding C algorithms, qsort and bsearch, are still useful in C++ programs for at least three reasons:
• Legacy code. Familiarity with C algorithms is needed to maintain legacy C code.
• Efficiency. You cannot apply STL algorithms to items that are not stored in an STL container. To apply these algorithms to a built-in array, you first have to copy it into a container--an operation that incurs runtime overhead.
• Applicability to non-OO data types. STL algorithms rely on operators == and >. However, these operators are either meaningless or not defined when applied to plain structs or built-in arrays. C algorithms do not rely on these operators to work.

94. The Copy Algorithm

The Standard Library provides a generic copy function, which can be used to copy a sequence of objects to a specified target. The first and the second arguments of copy() are const iterators that mark the sequence's beginning and its end, respectively. The third argument points to a container into which the sequence is copied. The following example demonstrates how to copy the elements of a list into a vector:

#include <algorithm>
using namespace std;

void main()
list<int> li; vector <int> vi;
li.push_back(1); // fill the list with elements
vi.reserve( li.size() ); // a vector must make room for copied elements in advance
copy (li.begin(), li.end(), vi.begin() ); //copy list elements into vector, starting at vector's beginning

95. std::string Should Never be Initialized With a NULL Pointer
You can initialize std::string with a pointer to char. However, it does not check the pointer. When the pointer happens to be NULL the results are undefined:

using std::string;

const char * getDescription(int symbol); // returns NULL when symbol cannot not found

void writeToString (string & str, int symbol){
str = getDescription(symbol); // sloppy: initializer might be NULL; undefined behavior in this case
To be on the safe side, you should explicitly check the pointer before you assign it:

void writeToString ( string & str, int symbol){
const char *p = getDescription(symbol);
if (p) {
str = p; // now safe
96. Various Forms of Initializing std::string
Class std::string can be initialized in five forms: by a C-string, by part of a C-string, by another string object, by a sequence of characters, or by part of another string object. For example:

#include <string>
using namespace std;
void f()
const char text[] = "hello world";
string s1 = text; // initialization of string object with a C-style string
string s2(s1); // initialize with another string object
string s3(&text[0], &text[5]); // initialize by part of a C-string
string s4(10, 0); // by a sequence of 10 characters
string s5 ( s2.begin(), s2.find(' ')); // by part of another string object; s5 = "hello"
97. Never Store Arrays of Objects in an auto_ptr
The Standard Library's auto_ptr class template automatically destroys an object that is allocated on the free store when the current scope is exited. For example:

#include <memory> //definition of auto_ptr
#include <string>
using namespace std;

int main()
auto_ptr<string> ps (new string);
*ps = "hello";

} //ps is automatically destroyed at this point
Note however, that using auto_ptr to for an array of objects results in undefined behavior because the destructor of auto_ptr calls plain delete (and never delete []) to destroy its bound object. As you know, only operator delete[] should be used in order to destroy an array of dynamically-allocated objects. Therefore, never store arrays of objects in an auto_ptr:

int main()
auto_ptr<string> ps (new string[2]); //bad

} //undefined behavior
98. Efficiency of STL or Quality of Implementation?
I often hear people ask whether it's possible to write code that is quicker than STL.
This is a paradoxical question: the C++ Standard defines minimum performance requirements with which STL containers and algorithms must comply. This doesn't mean that one cannot do better than that, and in fact, there are many STL implementations that utilize sophisticated optimizations to provide better performance (see for such an example) than the Standard's minimum requirements. Thus, it all boils down to an issue called "quality of implementation." Put differently, a better, faster, and slimmer STL implementation is still STL as long as it meets all the ANSI/ISO requirements. Thus, if you're not satisfied with the performance of your default STL package, you may switch to another vendor's STL package without having to switch to another compiler or IDE.
99. Where are std::min() and std::max()?

The Standard Library defines the two template functions std::min() and std::max() in the <algorithm> header. In general, you should use these template functions for calculating the min and max values of a pair. Unfortunately, Visual C++ does not define these function templates. This is because the names min and max clash with the traditional min and max macros defined in <windows.h>. As a workaround, Visual C++ defines two alternative templates with identical functionality called _cpp_min() and _cpp_max(). You can use them instead of std::min() and std::max().To disable the generation of the min and max macros in Visual C++, #define NOMINMAX before #including <windows.h>.

100. Volatile Semantics and Container Objects
Volatile objects are used in multithreaded applications and applications that map hardware devices into registers. Although you can declare an STL container object with the volatile qualifier, you will not be able to use any of its member functions safely. The problem is that none of the STL containers declares any volatile member functions. Yet calling a non-volatile member function on a volatile object is ill-formed. Consider the following example:

volatile vector <int> vi;
vi.push_back(1); // trouble; push_back() isn't volatile
Some compilers will compile this code with warnings; others will issue an error message. Either way, the effect of calling a non-volatile member function on a volatile object is unpredictable because the state of the object might be changed by another thread while the function is executing.
Is there a way to get around this? Probably not. By design, STL containers don't include volatile member functions because of the performance penalty and implementation complexity associated with volatile semantics.

No comments: