Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Tuesday, November 29, 2011

Assignment between Class Objects

Some short notes about assignment between class objects.

Suppose we are considering the assignment a = b, where a if of class A type, and b is of class B type.
  1. The overloaded operator= function will be looked first. If there is any, the assignment will call this function.

  2. If no overloaded operator= function, and a and b are of same class type, the assignment will be member-wise shallow copy.

  3. If no overloaded operator= function, and class B is derived from class A (in other words, b is an a), the assignment will copy the base class members.

  4. If no overloaded operator= function, and a and b are of unrelated class types, the assignment will try to call copy constructor if any.

  5. If no overloaded operator= function, a and b are of unrelated class types, and no copy constructor available, the assignment will try to call overloaded type casting function if any.

  6. If none of above applies, then the compiler will report an error about the assignment.

Related Posts:
Type Casting in C++
Type Casting on Classes
static_cast, dynamic_cast, and reinterpret_cast

Constructors and Destructors

This post looks into some details about class constructors and destructors. First look at a piece of code in the following.


What's going on really with above code? Which objects were constructed and destructed? What's the order and logic relationship? Below is the output by above program. (The comments were added for explanation.)
default constructor: 1st           // initialize f1 with value "1st"
before calling func ...
copy constructor: 1st              // construct local object f in func
within function func ...
copy constructor: 1st              // initialize f2, no temporary object returned
destructor: 1st                    // destroy local object f
after calling func ...
default constructor: 3rd           // initialize f3 with value "3rd"
default constructor: default       // initialize f4 with default value
default constructor: 4th           // temporary object with value "4th"
f4.buf: 4th                        // after shallow copy to f4
destroying temporary object ...
destructor: 4th                    // destroy temporary object
f4.buf:                            // f4.buf becomes invalid
destroying local objects ...
destructor:                        // destroy f4
destructor: 3rd                    // destroy f3
destructor: 1st                    // destroy f2
destructor: 1st                    // destroy f1

If no copy constructor was defined, the function call may bring some problems because of shallow copy. When exiting the function func, the destroy of local variable f will also make the buf member of returned object invalid.

Accessibility of Class Members

In this post, I briefly summarize the accessibility of class members in C++.

Accesspublicprotectedprivate
the same classyesyesyes
derived class
public inheritance
yes
still public
yes
still protected
no
still private
derived class
protected inheritance
yes
change to protected
yes
still protected
no
still private
derived class
private inheritance
yes
change to private
yes
change to private
no
still private
friendyesyesyes
otheryesnono

References:
C++ Tutorial

Related Posts:
Friend Functions and Classes

References in C++

Some notes about references in C++. Also, see the new standard introduced in C++11 about rvalue references and move semantics.
  1. It is not possible to refer directly to a reference object after it is defined; any occurrence of its name refers directly to the object it references.
  2. Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.
  3. References cannot be null, whereas pointers can; every reference refers to some object, although it may or may not be valid.
  4. References cannot be uninitialized. Because it is impossible to reinitialize a reference, they must be initialized as soon as they are created. In particular, local and global variables must be initialized where they are defined, and references which are data members of class instances must be initialized in the initializer list of the class's constructor.
  5. Most compilers will support a null reference without much complaint, crashing only if you try to use the reference in some way.
  6. References become invalid if they refer to an object with automatic allocation which goes out of scope.
  7. References become invalid if they refer to an object inside a block of dynamic memory which has been freed.
  8. Undefined behavior and invalid reference (initialized by dereferencing a null pointer):
    int *ip = 0;
    int &ir = *ip;
  9. References exhibit polymorphic capabilities, which is similar to pointers.
  10. References not qualified with const can only be bound to addressable values, not rvalues.
  11. References not qualified with const cannot be bound to temporary objects.
  12. Never return references to local or temporary objects.
  13. It is unspecified whether or not a reference requires storage.
  14. There shall be no references to references, no arrays of references, and no pointers to references.
  15. The declaration of a reference shall contain an initializer except when the declaration contains an explicit extern specifier, is a class member declaration within a class declaration, or is the declaration of a parameter or a return type.
  16. A reference cannot be bound directly to a bitfield.
  17. Given typedef int & iRef;, the declaration const iRef ir=5; is incorrect. Here, the qualifier const will be ignored. However, the following code is correct.
    typedef const int & ciRef;
    ciRef ir = 5;
  18. For pointers, there may be both const pointers and pointers to const variables; for references, there is no const references, at least not directly. By nature, reference is const and cannot be reassigned. In this sense, the term "const reference" always refers to "reference to const variable".

References:
Wikipedia: Reference
Rvalue References and Move Semantics

Monday, November 28, 2011

Function Template Specialization

A good study example for function template specialization.


Some notes about function template specialization:
  1. Explicit template specialization should be defined in namespace scope, and hence not inline within class body.
  2. Class templates can be partially specialized, but partial function template specialization is not allowed.
  3. The template parameter list after function name may be omitted, or some trailing arguments omitted. However, it is recommended to keep the <> pair.
  4. Template parameter list is part of signature for function templates and their specializations. Pay attention to the differences between function overloading and partial specialization.
    // Suppose we have the function template
    template< typename T1, typename T2 >
    void func( T1 v1, T2 v2 ) { ... }
    
    // The following is considered as function template overloading and allowed
    template< typename T >
    void func( T v1, T v2 ) { ... }
    
    // The following is considered as partial specialization and not allowed
    template< typename T >
    void func< T, T >( T v1, T v2 ) { ... }
    
Related Posts:
Function Signatures
Class Templates

Inheritance and Order of Constructor Calls

For the code segment below, how many times of base class constructor will be called?

  1. For single inheritance, the order is always the same: base class constructor first, then its own constructor.
  2. For multiple inheritance, call the virtual base constructor first if any, then follow the order from left to right for non-virtual base constructors, and finally call its own constructor. Note that only a unique set of virtual base constructors will be called, which is exactly the purpose of virtual inheritance.

Example 1:
Output 1:
Base constructor
Foo1 constructor
Base constructor
Foo2 constructor
Multi constructor

Example 2:
Output 2:
Base constructor
Foo1 constructor
Base constructor
Foo2 constructor
Multi constructor

Example 3:
Output 3:
Base constructor
Base constructor
Foo1 constructor
Foo2 constructor
Multi constructor

Example 4:
Output 3:
Base constructor
Foo1 constructor
Foo2 constructor
Multi constructor

Sunday, November 27, 2011

static_cast, dynamic_cast and reinterpret_cast

See also the posts (1, 2) on type casting.
  1. static_cast can be used to cast between fundamental types.
    int i;
    float f;
    i = static_cast<int>(f);
    
  2. static_cast cannot be used to cast between pointers or references to fundamental types.
    int *ip, &ir;
    float f;
    ip = static_cast<int*>(&f);   // illegal
    ir = static_cast<int&>(f);    // illegal
    
  3. static_cast can be used to cast between class types if one of the class types has explicit conversion constructor or type casting operator function. For this case, static_cast is the same as explicit casting using parentheses or implicit casting using assignment.
  4. static_cast can be used to cast derived class type to base class type (public inheritance only), but cannot be used to cast base class type to derived class type if no defined conversion constructor or casting operator. Think about function arguments and exception catch, which are implicit type casting.
  5. static_cast can be used to cast between pointers or references to related classes (base and derived, public inheritance only). The compiler won't complain if casting base class pointer to derived class pointer. The overhead of type-safe check by dynamic_cast can be avoided. It's the programmers' responsibility to ensure safe conversions.
  6. static_cast cannot be used to cast for other cases, including between fundamental types and class types, between pointers or references to fundamental types and class types, or between pointers or references to unrelated classes.
  7. dynamic_cast can be used to cast pointers or references to related classes only (base and derived, public inheritance only). In other words, dynamic_cast won't work on non-pointer/reference types, or pointer/reference to fundamental types.
  8. When casting between pointers or references to unrelated classes, dynamic_cast will pass compile, but there will be run-time error.
  9. dynamic_cast can be always successful if casting derived class to base class (public inheritance only).
  10. When casting from base class to derived class, dynamic_cast will pass compile only when the base class is polymorphic. But there will be run-time error.
  11. dynamic_cast cannot be used to cast base class to derived class if the base class is not polymorphic. (compile error)
  12. reinterpret_cast can be used to cast between any pointer or reference types, even between those to unrelated classes or fundamental types or mix.
    int *ip, &ir;
    float f;
    ip = reinterpret_cast<int*>(&f);
    ir = reinterpret_cast<int&>(f);
    
  13. When using reinterpret_cast, neither the content pointed nor the pointer type itself is checked, and thus dereferencing it is unsafe.
  14. reinterpret_cast even works on any kind of inheritance, either public, protected, or private.
  15. reinterpret_cast can be used to cast between pointer types and integer types.

To demonstrate, I wrote a sample code.


Related Posts:
Type Casting in C++
Type Casting on Classes
Assignment between Class Objects

Thursday, November 24, 2011

Object Initialization for User-Defined Classes

First of all, to declare or initialize class objects, the class must have default constructors, either explicitly defined in source code or implicitly defined by the compiler. Note that a default constructor is a constructor that either has no parameters, or if it has parameters, all the parameters have default values.

When declaring objects (not references or pointers), we can either directly declare an object with no explicit initializer or parenthesis, or explicitly provide initializer with parenthesis. Note that although the default constructor was defined and we may call general default functions without providing any arguments, we cannot initialize objects by such function call scheme. In other words, we may not provide a pair of parenthesis while no explicit initializer arguments.

Suppose class SomeClass has default constructor, then
SomeClass object1;                 // legal
SomeClass object2(initializer);    // legal
SomeClass object3();        // illegal (compiler won't complain, 
                            // but it will consider object3 as a function)

When initialize object pointers using keyword new, there will be different behaviors for explicitly and implicitly defined default constructors.

For classes with explicitly defined default constructor, the following 2 schemes work exactly the same way.
SomeClass *pointer1 = new SomeClass;       // call default constructor
SomeClass *pointer2 = new SomeClass();     // call default constructor

However, for classes without explicitly defined default constructor, the 2 schemes differ in the initial value for object data members.
AnotherClass *pointer1 = new AnotherClass;       // undefined data members
AnotherClass *pointer2 = new AnotherClass();     // nullified data members

I wrote a sample code and tested with both g++ and MSVC, and I am attaching the output by MSVC in this post after the code.




Related Posts:
Array Declaration and Initialization
new and delete

Type Casting on Classes

In one previous post, I discussed about general type casting in C++. While in this post, I am going to talk about the casting on classes. More specifically, I won't discuss the keyword approach (static_cast, dynamic_cast, reinterpret_cast or const_cast) in this post.

Based on the is-a relationship between derived and base classes, implicit casting from derived class to base class is always possible for public inheritance (not possible for protected or private inheritance).
class Base {};
class Derived: public Base {};

Derived d;
Base b = d;

Without use of keywords, we may have 3 ways to convert unrelated class types.
  1. Corresponding constructor
  2. Overload assignment operator
  3. Overload type casting operator
Suppose we want cast an object of class A to class B, then we may need define 1 or 2 in B's body, while define 3 in A's body.

Here is a sample code I wrote for testing. Note that the "=" sign in declaration is not assignment operator. Initialization, assignment, and explicit casting are all legal for Approaches 1 and 3, but only implicit casting by assignment is legal for Approach 2 with overloaded operator "=".



Related Posts:
Type Casting in C++
static_cast, dynamic_cast, and reinterpret_cast
Assignment between Class Objects

Wednesday, November 23, 2011

getline and istream::getline

In C++, these 2 functions both read a string of characters from an input stream. I didn't pay attention to their differences, and in this post I summarize about that.

First, let's look at the function signatures.

istream& getline( istream& is, string &str );
istream& getline( istream& is, string &str, char delim );

istream& istream::getline( char *s, streamsize n );
istream& istream::getline( char *s, streamsize n, char delim );

For both functions, the delimiter character is delim and '\n' (newline character) is the default value. The extraction also stops if the end of file is reached or if some other error occurs during the input operation. For the function istream::getline, at most \(n-1\) characters can be read and the ending null character will be automatically appended to the C-style string after data extraction.

For both functions, if the delimiter is found, it is extracted and discarded, i.e. it is not stored and the next input operation will begin after it.

Differences:
  1. getline is a global function included in the header <string>, while istream::getline is a member function of class istream.
  2. getline reads characters into a C++ string type string, while istream::getline reads characters into a C-style string.

References:
C++ Reference: getline
C++ Reference: istream::getline

Tuesday, November 8, 2011

Variable Length Argument List

Sometimes we need pass along an unknown number of arguments before actually calling the function, such as searching for the minimum of maximum of a list of numbers, or calculating their average. Most programming language are able to first construct an array or list and then pass the constructed object as a single argument. But we can also pass an undefined length of arguments, and this post just summarizes this approach. Different languages provide different ways to deal with this case, and here I am discussing C/C++, Java and Python.

In C/C++, we need the type va_list and macros va_start, va_arg, and va_end. All these are included in the header file <stdarg.h> or <cstdarg>. Below is an example.

In Java, however, we directly append an ellipse after the type name, and then we can give a variable name (which is actually is an array and can be iterated via enhanced for loop in Java).

In Python, programmers have more power than passing an array of arguments: it allows a dictionary of arguments, by which we actually passing both a set of identifier names and their corresponding values. This offers more options to use the variable length arguments.

In summary,
  1. The variable length parameters are always placed at the end of parameter list of the function.
  2. In C/C++, at least one parameter must be passed and declared in the list. There is no such requirement in Java or Python. Also, in C/C++, the comma or space is not required after the last given parameter.
  3. In C/C++, the variable arguments are able to be converted to different types via va_arg, and also this conversion is required for each argument. However, Java will first construct an array of the same type for all the passed arguments. Of course, you can cast the elements if necessary.
  4. In Python, the arguments in calling function may become a little bit complex. The general order is that non-keyword arguments should be placed before keyword arguments. Also, there may be explicitly defined or predefined tuple or dict that will be passed as variable arguments, and in general these are always placed after "in-place" arguments. Here is an example.
    def: foo(arg1, arg2, *nonkws, **kws):
        #### function body
    
    pre_Tuple = (pre_nonkw1, pre_nonkw2)
    pre_Dict = {'pre_kw1':pre_v1, 'pre_kw2':pre_v2}
    
    # call function foo()
    foo(val1, val2, nkw1, nkw2, kw1=v1, kw2=v2, *(expl_nkw1, expl_nkw2), **{'expl_kw1':expl_v1, 'expl_kw2':expl_v2})
    or
    foo(val1, val2, nkw1, nkw2, kw1=v1, kw2=v2, *pre_Tuple, **pre_Dict)

    The only possibility to reorder the argument list is
    foo(val1, val2, nkw1, nkw2, *(expl_nkw1, expl_nkw2), kw1=v1, kw2=v2, **{'expl_kw1':expl_v1, 'expl_kw2':expl_v2})
    or
    foo(val1, val2, nkw1, nkw2, *pre_Tuple, kw1=v1, kw2=v2, **pre_Dict)

    Explicitly defined tuple or dict cannot co-exist with predefined tuple or dict. Only one for each is possible.

Monday, November 7, 2011

Class and Struct

In C, there is no such special thing called class, and class is just an ordinary identifier. For example,
int class=2012;
The struct in C contains data members only, and all of them are visible in the same scope as struct itself.

In Java, there is no such thing called struct, but we can use class to implement struct. The class may contain member functions besides data fields, and programmer is able to declare the visibility for each member.

Interesting while kind of confusing, C++ has them both: class and struct. In many ways, C++ compilers treat them the same. Below are some short notes about class and struct in C++.

  1. Both class and struct can have member data and member functions, and are able to specify their visibilities.
  2. Both class and struct can inherit from and be inherited by another class or struct. To state more clearly, class can inherit from either class or a struct, and similarly, struct can inherit from either class or struct.
  3. Both class and struct may contain pointers to the same class/struct type, but can't contain objects or references of the same class/struct type.
  4. Both keywords class and struct can be omitted when declaring objects. While in C, the keywords struct, as well as union and enum, can not be omitted when declaring unless a typedef was used or anonymous variable is being declared.
  5. C++ compilers don't distinguish the keywords class and struct in terms of type name. In other words, you may not define a class with name A and then define a struct with name A. The compilers will report "redefinition error". This is also true for struct, union and enum in C, although the keywords must be included for declaration.
  6. The members of a class are private by default, and the members of a struct are public by default.
  7. The inheritance for a class is also private by default, and the inheritance for a struct is public by default.
  8. In C and C++, all definitions for class, struct, union and enum must end with a semicolon ;, while in Java, no such ; outside the closing brace for class and enum definitions. (There is no struct or union in Java.)

Sunday, November 6, 2011

Dynamic Memory Allocation with new and delete

Some notes about the usage of keywords new and delete.

Syntax:
type *p = new type;     // undefined initial value for user-defined type
type *p = new type();   // default initial value, 0 for user-defined type
type *p = new type(initializer);
type *p = new type[size];          // default constructor
type *p = new type[size]();        // also default constructor
type *p = new (nothrow) type;      // don't throw bad_alloc exception
type *p = new (nothrow) type();
type *p = new (nothrow) type(initializer);
type *p = new (nothrow) type[size];
type *p = new (nothrow) type[size]();
delete p;
delete[] p;
Notes:
  1. Initializers cannot be specified for arrays created with new. All elements of an array will be initialized with the default constructors. If the type doesn't have a default constructor, this will be a compile-error.
  2. The behavior when operator new fails is compiler-specific. Most compilers will throw a std::bad_alloc exception. Using nothrow as above will return 0 or NULL and continue the program when new fails. Another option is to use function set_new_handler to handle new failures. The header file <new> is needed for the use of bad_alloc, nothrow, and set_new_hanlder.
  3. The statement delete NULL; is allowed and no error will be reported.
  4. In order to avoid double-free problem, assign the pointer to NULL after deletion.
    delete p;
    p = NULL;
    or
    delete[] p;
    p = NULL;
  5. It is not possible to directly reallocate memory allocated with new[ ]. To extend or reduce the size of a block, one must allocate a new block of adequate size, copy over the old memory, and delete the old block.
  6. Arrays allocated with new[ ] must be deallocated with delete[ ], not delete. Since the layout of arrays allocated with new[ ] is implementation defined, and possible not compatible with new. Some implementations of new[ ] embed the number of allocated objects first into the beginning of the allocated memory chunk, and return pointer to the remaining part of the array.

Related Posts:
Array Declaration and Initialization
Object Initialization

Tuesday, November 1, 2011

Size of Class Objects

Some notes about how to determine the size of class objects in C++.

  1. The keyword sizeof can work on both class type names and class objects.
  2. Static member data don't contribute to the size of class (they are classwide available)
  3. Member functions don't really contribute to the size of class (except the vtable for virtual functions).
  4. The this pointer doesn't contribute to the size of class objects.
  5. Friends and pointers to members are not class members at all, and hence they won't contribute to the class size.
  6. It is unspecified whether or not a reference requires storage. It may be compiler specific.
  7. The actual size is most likely greater than the sum of sizes for each non-static member data and virtual pointers. This is because of byte alignment or padding, and depends on the compiler.
  8. Virtual pointer will take some space if there are virtual functions or virtual inheritance in the class.
  9. Empty classes won't be size 0 in order to distinguish the objects of that class type. By most compilers, their size will be 1.
  10. Empty sub-objects will take no space in memory when an empty class is inherited by a non-empty class type.
  11. Regular inheritance will make the size of derived class the sum of all non-static member sizes from its base classes.
  12. The _vptr field is always an alias of the first available _vptr of its base classes, if there are some. Otherwise, the class itself will create a new _vptr if necessary.
  13. The virtual inheritance is kind of complex. It's designed to solve the diamond problem in inheritance, and there will be only 1 copy for the common ancestor members. Simply speaking, the class will first create the non-virtual base class members, and its own members in class body, and finally a unique set of members from virtual base classes (including immediate virtual base and inherited virtual base).

References:
C Programming
C++ FAQ
Wikipedia: Reference
Wikipedia: Virtual Inheritance

Monday, October 31, 2011

The Keyword typedef

Some usages for the keyword typedef.

  1. Introduce a synonym for fundamental types.
    typedef int Integer;
  2. Introduce a synonym for pointer types.
    typedef char *pChar;
  3. Introduce a synonym for reference types.
    typedef double &refDouble;
  4. Introduce a synonym for struct, union, enum in C.
    typedef struct {int key; NODE *next;} NODE;
  5. Introduce a synonym for struct or class in C++.
    typedef class Old New;
    typedef Old New;
  6. Introduce a synonym for array types.
    typedef double dArray[10];
  7. Introduce a synonym for function types.
    typedef void Func(int, double);
  8. Introduce a synonym for function pointer types.
    typedef (void *)FuncPtr(int, double);

Pointers to Members in C++

Rarely used. Just keep a memo.

Example:
class A {
public:
    int x;
    char *s;
    void f( float );
};

int main()
{
    int A::*xp = &A::x;
    char *A::*sp = &A::s;
    void (A::*fp)(float) = &A::f;

    A a;
    A *ap = &a;

    a.*xp ...
    a.*sp ...
    (a.*)fp( 0.0 ) ...

    ap->*xp ...
    ap->*sp ...
    (ap->*)fp( 0.0 ) ...

    ...
    return 0;
}

Notes:
  1. Pointers to members cannot be pointed to static members.
  2. The address stored in pointers to members are offset to the class object address.
  3. Pointers to members must be used with class objects.
  4. Pointers to member functions cannot be used to show function address. Used to call function only.

References:
IBM publib

Friend Functions and Classes in C++

Some notes for friendship in C++.

  1. Friends of a class can access any member of the class, either public, protected, or private.

  2. Friends of a class can be declared as different scopes.
    1. Global functions: any object or function that is able to call the global function may get access to the class contents.
    2. Global classes: all member functions of such global class may have access to the protected and private fields of the class declaring friend.
    3. Members of other class: this will specify which member function or class of the "friend" class can have access to the class contents.

  3. Friendship is not inherited, reciprocal, or transitive.
    1. If class A is friend of B and C is derived from B, A may not be friend of C unless explicitly declared.
    2. If class A is friend of B and C is derived from A, C may not be friend of B unless explicitly declared.
    3. If class A is friend of B, B may not be friend of A unless explicitly declared.
    4. If class A is friend of B and B is friend of C, A may not be friend of C unless explicitly declared.

  4. Friends are not member of the class.
    1. The declaration of friends can be put anywhere, either public, protected, or private.
    2. The this pointer is not available in friends.

  5. A friend function or class can be the friend of multiple classes, hence they can be used for message passing between classes.

  6. The definition of friends can be placed in the body of class who declares the friendship. Even in this case, the friend functions or classes are in the global scope, not members of the class.

  7. Do friends violate encapsulation?
    No! If they're used properly, they enhance encapsulation. Many people think of a friend function as something outside the class. Instead, try thinking of a friend function as part of the class's public interface. A friend function in the class declaration doesn't violate encapsulation any more than a public member function violates encapsulation: both have exactly the same authority with respect to accessing the class's non-public parts.

References:
Coding Unit Tutorials
C++ FAQ

Operator Overloading in C++

Just wrap-up the notes for operator overloading in C++.

  1. Almost all operators can be overloaded in user-defined classes, except the following four.
    .   .*   ::   ?:
  2. Some operator overloading can be defined as class member function only.
    ()   []   ->   "any assignment operators"   "type conversion"
    Question: how about ->*?

  3. In some cases, global functions are needed to implement operator overloads.
    1. When using stream operator on a user-defined class, it may be impossible to overload the cout's member operator functions for a general programmer. In this case, global functions is needed.
    2. When defining some operations requiring commutability, the global function approach is necessary, since class member functions always take the object itself as the first implicit argument.

  4. Prefix increment and decrement overloads are exactly the same as any other unary operators. For postfix increment and decrement, the compiler will generate a function call operator++ with one int argument 0 (or besides the class object parameter in global functions).

  5. Type casting operations are possible using operator overloading. For example,
    A::operator int() const;
    A::operator B() const;   // class B should be defined/declared before class A
    Note that the return type may not be specified on a type casting function.

Sunday, October 30, 2011

Type Casting in C++

There are several ways to cast one type to another in C++. I'd like to categorize them as implicit conversion, explicit conversion, and keyword conversion (which is also explicit, and I separate it for discussion).

Implicit Conversion

As in C, some casting can be automatically done for some compatible types.
int a = 100;
double b;
b = a;
Also, such implicit conversion occurs when initialize or assign to class objects, calling the overloaded "=" operator or constructor.
class A {};
class B { public: B(A a){} };
A a;
B b = a;

Explicit Conversion
Like in C, we can use parentheses to cast types. The ways for explicit casting include:
(new_type) expression
new_type (expression)
(new_type) (expression)
Some notes for this:
  1. At least one pair of parentheses is required.
  2. If the new type is pointer, reference, or contains multiple words (like const char), the parentheses for new_type is required.
  3. If the expression is not a single variable or number (like a+b, the parenthesis for expression is required.
  4. This explicit casting applies to almost any types (including class types with explicit constructor or operator function), but there may be errors in run time.

Keyword Conversion
Four keywords are available for casting: static_cast, dynamic_cast, reinterpret_cast, const_cast. The usage for keyword conversion is
static_cast<new_type>(expression)
dynamic_cast<new_type>(expression)
reinterpret_cast<new_type>(expression)
const_cast<new_type>(expression)
Note: the parentheses for expression are always required, even when the expression is a single variable or number.

static_cast
  1. It can be used to cast between fundamental types, as well as class types for those with explicit constructors or operator functions.
  2. It can perform casting between pointers or references to related classes. The compiler won't complain if casting base class pointer to derived class pointer. The overhead of type-safe check by dynamic_cast can be avoided. It's the programmers' responsibility to ensure safe conversions.
dynamic_cast
  1. It can be used only with pointers or references to objects. Its purpose is to ensure that the result of the type conversion is a valid complete object of the requested class.
  2. Dynamic casting from derived class to base class is always successful, while from base class to derived class, successful only when the base class is polymorphic.
  3. It requires the Run-Time Type Information (RTTI) to keep track of dynamic types. Some compilers support this feature as an option which is disabled by default. This must be enabled for runtime type checking using dynamic_cast to work properly.
reinterpret_cast
  1. It converts any pointer type to any other pointer type, even of unrelated classes.
  2. All pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked, and thus dereferencing it is unsafe..
  3. It can also cast pointers to or from integer types.
const_cast
  1. It manipulates the constness or volatileness of an object, either to be set or to be removed.

References
C++ Tutorial

Related Posts:
Type Casting on Classes
static_cast, dynamic_cast, and reinterpret_cast
Assignment between Class Objects

Friday, October 28, 2011

Function Pointers in C++

Just discovered some interesting facts about function pointers in C++. Here I am giving a short summary.

Different compilers may treat function pointer operations in different ways. For the output operation cout <<, MSVC10 can print the actual address for general function pointers, but g++ will print a bool value 1. However, if casted to void * type, both MSVC and g++ can display the actual address.

For class member functions, MSVC works the same way as g++, and both would output a bool value without casting. One more point is that C++ standard does not allow class member pointers be casted to void * type. Therefore, we have no way to get the real address unless we use the printf function.

Here are a sample program and its outputs by MSVC and g++.

Output by MSVC:
Output by g++:

======================================
Some more thinking about this 2 days after the original posting. I just found another special case in which both MSVC and g++ work exactly the same way for function pointers. Also, this may explain why g++ prefers to output a bool value for general function pointers in the statement cout << fp.

The special case is related to stream manipulator! Actually, stream manipulators are function pointers with the prototype ostream & manipulator( ostream & ). Seems that C++ library has already overloaded the case cout.operator<<( ostream & func(ostream &) ) and it will actually execute func( ostream & ) and returns ostream &. For all other function names or function pointers, it will return a bool value.