Saturday, May 31, 2008

C++ #21

101. Built-in types have a constructor
In standard C++ built-in types such as char, int and float can be initialized like any ordinary user-defined type:

char *pc = new char('a');
int pi= new int(10);
float pf = new float(0.333);
This language extension was introduced to the Standard in order to be compatible with STL containers and algorithms

102. Vector Elements are not Guaranteed to Occupy Contiguous Memory
Built-in arrays in C++ reside in contiguous chunks of memory. The Standard, however, does not guarantee that elements of a vector occupy contiguous memory. All current STL implementations store vector elements in contiguous memory, but it's just a convention, not a requirement.

103. STL Iterators are not Necessarily Pointers
Most implementations of STL (Standard Template Library) use pointers as the underlying representation of iterators. However, an iterator doesn't have to be a pointer, and there's a good reason for that. Consider a huge vector of scanned images stored on a 6-Gigabyte disk. A built-in pointer on most machines has only 32 bits, which wouldn't be large enough to iterate through such a large vector. Instead, the implementer can use a 64-bit integer as the underlying iterator in this case.

104. Const Iterators
You can think of an iterator as a pointer to elements of a container. All STL (Standard Template Library) containers provide the basic iterator type along with its const counterpart. The const iterator of a container should be used when the elements accessed through it are not supposed to be modified, like in the following example:

void main() {
string s ("hello world"); // s contains 11 characters
string::const_iterator p = s.begin(); //p is a const iterator pointing to the first element of s
size_t n = 0;

while ( p < s.end() ) { // a loop that count the number of chars in s
n++;
p++; // move to the next element of s
}
cout<<n;
}

105. The begin() and end() Member Functions of STL Containers

All STL (Standard Template Library) containers provide the begin()/end() pair of member functions. The member function begin() returns an iterator that points to the first element of the container:

vector < int > v(1); // create a vector of int with one element
v[0] = 10; // assign a value to the first element of v
vector<int>::const_iterator p = v.begin(); // p points to the first element of v
cout << *p; //output 10
The member function end(), however, returns an iterator pointing one position past the last valid element of the container. This may sound surprising at first, but there's nothing really unusual about it if you consider how strings in C are represented. An additional null character is automatically appended one position past the final element of the char array. The additional element in STL has a similar role; it denotes the end of the container, and is automatically appended to the container.

106. What's in an Allocator?
Every STL container uses an allocator that encapsulates the memory model that the specific platform uses. Allocators hide the platform-dependent details such as the size of pointers, memory organization, reallocation model, and memory page size. Since a container can work with different allocator types, it can easily work in different environments simply by plugging a different allocator to it. Every implementation provides a suitable allocator by default. Normally, users should not override the default allocator but they may do so for special purposes.

Calss
107. Const and Reference Data Members Must be Initialized
A class may have const and reference data members. These members must be explicitly initialized by a member-initialization list:

class Allocator
{
private:
const int chunk_size;
const string & serialization_path; // file into which an object can be serialized
public:
Allocator(int size, const string& path) : chunk_size(size), serialization_path (path){}
};

108. Construct an Object on a Pre-Allocated char Array
The memory buffer returned by operator new is guaranteed to have the suitable alignment properties for any type object. This property enables you to construct objects on such a pre-allocated char array. For example:

#include <new>
#include <iostream>
#include <string>
using namespace std;

class Employee{ /*...*/};

void f(){
char * pc = new char[sizeof(Employee)];
Employee *pemp = new (pc) Employee; //construct on properly aligned char array
//...use pemp
pemp->Employee::~Employee(); //explicit destruction
delete [] pc;
}

109. Prefer Initialization to Assignment of Objects
In general, assignment requires the creation and destruction of a temporary object, whereas initialization does not. This is true for not just for strings, but for every object type, so whenever you have the choice--prefer initialization to assignment. Consider this code snippet:

string s1 = "Hello ", s2 = "World", both;
both = s1+s2; // assignment rather than initialization; inefficient
The compiler transforms the expression both = s1+s2; into:

string temp (s1 + s2); // store result in a temporary
both = temp; // assign
temp.Date::~Date(); // destroy temp
This snippet could have been written in a much more efficient way:

string s1 = "Hello ", s2 = "World";
string both = s1+s2; // initialization is more efficient than assignment
This time, the compiler transforms the expression both = s1+s2; into:

string both (s1 + s2);
110. Destructors Should Handle Exceptions Locally
It is legal to call a function that may throw an exception inside a destructor. However, it is important to handle any exception that might be thrown from such a function inside the destructor body.

void cleanup() throw (int);

class C {
public:
~C();
};

C::~C() {
try {
cleanup();
}
catch(int) {
//handle the exception
}
}
The exception thrown by cleanup() is handled inside the destructor. Otherwise, the thrown exception will propagate outside the destructor, and if the destructor has been invoked while unwinding the stack due to another exception, terminate() will be called.

111. A String Literal Cannot be Used as a Template Argument
A template can take as an argument the address of an object with external linkage. A string literal cannot be used as a template argument since it has internal linkage. For example:

template <class T, const char *> class A
{/*…*/};
const char *global_ptr;
void array_user()
{
const char * p ="illegal";
A<int, "invalid"> aa; // error, string literal used as a template argument
A<int, p> ab; // also an error, p has internal linkage
A<int, global_ptr > ac; // OK
}
112. Defining Static Data Members of a Template
Templates can have static data members. A definition for a static data member can appear in a namespace scope enclosing the definition of the class template that contains the static member. For example:

template<class T> class C
{
public:
static T stat; //declaration
};
template<class T> T C<T>::stat = 5; //definition
A static data member can be accessed like this:

void f()
{
int n = C<int>::stat;
}
113. Use a Using-Declaration Instead of an Access Declaration
The access specifier of a base class member can be changed in a derived class by an access declaration, as in this example:

class A
{
public:
int n;
};

class D : public A
{
private:
A::n; // access declaration changes access of A::n to private; deprecated
};
According the ANSI/ISO Standard, the use of access declarations is considered deprecated. Instead, you should use a using declaration for that purpose:

class D : public A // using-declaration version
{
private:
using A::n; // using declaration changes the access of A::n to private
};

No comments: