Member Functions

Member functions implement the behavior of a class. Member functions can be defined within the class definition or separately. You can use the inline function specifier and either the static or virtual (but not both) specifier. (See Chapter 2 for more about function specifiers.) Defining a member function within the class definition declares the function inline, even if you do not use the inline specifier.

A nonstatic member function can have const, volatile, or both function qualifiers. Qualifiers appear after the function parameters and before the exception specification. Function qualifiers are discussed in the next section, Section 6.3.2.

Example 6-8 shows various member function declarations and definitions.

Example 6-8. Declaring and defining member functions

#include <cmath>
#include <iostream>
#include <istream>
#include <ostream>

class point {
public:
  typedef double value_type;
  // Constructors are special member functions.
  explicit point(value_type x = 0.0, value_type y = 0.0);
  value_type x(  )       const { return x_; }
  value_type y(  )       const { return y_; }
  void x(value_type x)         { x_ = x; }
  void y(value_type y)         { y_ = y; }

  value_type distance(  ) const;
  bool operator==(const point& pt) const;

  inline static point origin(  );
private:
  value_type x_, y_;
};

point::point(value_type x, value_type y)
: x_(x), y_(y)
{}

point::value_type point::distance(  )
const
{
  return std::sqrt(x() * x() + y(  ) * y(  ));
}

bool point::operator==(const point& pt)
const
{
  return x() == pt.x() && y(  ) == pt.y(  );
}

inline point point::origin(  )
{
  return point(  );
}

int main(  )
{
  point p1;
  point::value_type n;
  std::cin >> n;
  p1.x(n);
  std::cin >> n;
  p1.y(n);
  if (p1 == point::origin(  ))
    std::cout << "Origin\n";
  else
    std::cout << p1.distance(  ) << '\n';
}

When defining a function inside a class definition, the entire function definition is in the scope of the class. Thus, name lookup for the return type and all parameter types looks first in the class, then in all base classes, and then in the surrounding namespaces. (See Chapter 2 for more information about name lookup.)

If the function definition is outside the class definition, the function return type is at namespace scope. Thus, if the type is a nested type in the class, you must fully qualify the type name. The function name must also be qualified so the compiler knows which class contains the function. After the compiler has seen the class name that qualifies the function name, it enters the class scope. Parameter types are then looked up in the class scope.

The address of a static member is no different than the address of an ordinary function or object at namespace scope. The address of a nonstatic member that is taken in a member function with an unqualified name is also an ordinary address. The address of a nonstatic member taken with a qualified member name (e.g., &cls::mem), however, is quite different from any other kind of address. The address even has a special name: pointer-to-member . You cannot use reinterpret_cast<> to cast a pointer-to-member to or from an ordinary data pointer or function pointer.

A pointer-to-member does not necessarily point to a particular function or object. Instead, think of it as a handle that keeps track of which function or data member you have selected but does not refer to a specific object. To use a pointer-to-member, you must use the .* or ->* operator, which requires an object or pointer as the lefthand operand and the pointer-to-member as the righthand operand. The operator looks up the member in the object and then obtains the actual data member or member function. (See Chapter 3 for more information about pointer-to-member expressions.) Example 6-9 shows one use of a pointer-to-member.

A nonstatic member function can be called only for an object of its class or for a derived class. The object is implicitly passed as a hidden parameter to the function, and the function can refer to the object by using the this keyword, which represents an rvalue pointer to the object. That is, if the object has type T, this is an rvalue of static type T*. In a call to a virtual function, the object's dynamic type might not match the static type of this.

Static member functions do not have this pointers. (See the next section, Section 6.3.3.)

If the function is qualified with const or volatile, the same qualifiers apply to this within the member function. In other words, within a const member function of class T, this has type const T*. A const function, therefore, cannot modify its nonstatic data members (except those declared with the mutable specifier).

Within a member function, you can refer to data members and member functions using just their names, and the compiler looks up the unqualified names in the class, in its ancestor classes, and in namespace scopes. (See Chapter 2 for details.) You can force the compiler to look only in the class and ancestor classes by explicitly using this to refer to the members—for example, this-> data and this-> func ( ). (When using templates, an explicit member reference can reduce name lookup problems; see Chapter 7 for details.)

Example 6-10 shows some typical uses of this.

A static member function is like a function declared at namespace scope; the class name assumes the role of the namespace name. Within the scope of the class, you can call the function using an unqualified name. Outside the class scope, you must qualify the function name with the class name (e.g., cls ::member) or by calling the function as a named member of an object (e.g., obj.member). In the latter case, the object is not passed as an implicit parameter (as it would be to a nonstatic member function), but serves only to identify the class scope in which the function name is looked up.

Static member functions have the following restrictions:

Example 6-11 shows some uses of static member functions.

Constructors and destructors are special forms of member functions. A constructor is used to initialize an object, and a destructor is used to finalize an object.

A constructor's name is the same as the class name. If you use typedef to create a synonym for the class name, you cannot use the typedef name as the constructor name:

struct point {
  point(int x, int y);
...
typedef point p;

p::point(int x, int y)     {  . . .  } // OK
point::point(int x, int y) {  . . .  } // OK
p::p(int x, int y)         {  . . .  } // Error
point::p(int x, int y)     {  . . .  } // Error

A constructor cannot have a return type, and you cannot return a value. That is, you must use a plain return; or return an expression of type void. A constructor cannot have const or volatile qualifiers, and it cannot be virtual or static.

Constructors can initialize data members with a list of member initializers, which appear after the function header but before the function body. A colon separates the header from the comma-separated initializers. Each initializer names a data member, an immediate base class, or a virtual base class. The value of the initializer follows in parentheses:

class-name(parameters)
: member(expr), base-class(expr-list), ...
compound-statement

You can also use a function try block, which wraps the constructor initializers as part of the try block. (See Chapter 4 for more information about try blocks and exception handlers.) The syntax is:

class-name(parameters)
try
  : member(expr), base-class(expr-list), ...
  compound-statement
                  exception-handlers

You can initialize a base class by invoking any of its constructors, passing zero or more expressions as arguments. See Section 6.4 later in this chapter for more information.

A constructor can be declared with the explicit specifier. An explicit constructor is never called for an implicit type conversion, but only for function-like initialization in a declaration and for explicit type casts. See the next section for more information.

Constructors are special because you never call them directly. Instead, the compiler calls the constructor as part of the code it generates to create and initialize an object of class type. Objects can be created in several different ways:

In each case, memory is set aside for the object, then the constructor is called. The constructor is responsible for initializing the object's members. Members that are listed in the initializer list are initialized as requested: scalar and pointer types are initialized with their given values; class-type objects are initialized by invoking the appropriate constructors. Class-type objects that are not listed are initialized by calling their default constructors; other types are left uninitialized. Every const member and reference-type member must have an initializer (or you must be willing to accept the default constructor for a class-type const member). Within the body of the constructor, you are free to change any non-const member. Many simple constructors are written with empty bodies:

struct point {
  point(int x, int y) : x_(x), y_(y) {}
  point(  )             : x_(0), y_(0) {}
  point(double radius, double angle) {
    x = radius * cos(angle);
    y = radius * sin(angle);
  }
  ...
Calling constructors

If a constructor is declared with the explicit specifier, it can never be called for an implicit type conversion. An implicit type conversion is commonly used during assignment-like construction. In the following example, two constructors are called: complex(1.0, 0.0) is implicitly called to construct a nameless temporary object, then the copy constructor is called to copy the temporary object into p. (The compiler is allowed to optimize away the copy and initialize p directly. Most modern compilers perform this optimization, but point must have a copy constructor even if it is never called.)

struct complex {
  complex(double re, double im = 0.0);
  ...
};
complex z = 1;

Implicit type conversions can produce some surprising results by calling conversion constructors when you least expect it. In the following example, a nameless temporary complex object is created as complex(42.0, 0.0), and this temporary object is bound to the z parameter in the call to add2.

complex add2(const complex& z) {
  z.real(z.real(  ) + 2);
  return p;
}
z = add2(42.0);

To avoid unexpected constructor calls, declare a constructor with the explicit specifier if that constructor can be called with a single argument. Calls to an explicit constructor require function-like construction or an explicit type cast:

struct complex {
  explicit complex(double re, double im = 0.0);
  ...
};
complex z = 1;                   // Error
complex z(1);                    // OK
add2(42);                        // Error
add2(static_cast<complex>(42));  // OK
add2(p);                         // OK

A constructor can also use a function try block as its function body. The try keyword comes before the initializers. If an exception is thrown during the initialization of any member, the corresponding catch blocks are examined for matching handlers in the usual manner. (See Chapter 4 for information about try blocks and exception handlers.) Example 6-12 shows a constructor with a function try block.

Regardless of whether the function body is a plain compound statement or a function try block, the compiler keeps track of which base-class subobjects and which members have been initialized. If an exception is thrown during initialization, the destructors of only those objects that have been fully constructed are called.

If the object is being created as part of a new expression, and an exception is thrown, the object's memory is deallocated by calling the appropriate deallocation function. If the object is being created with a placement new operator, the corresponding placement delete operator is called—that is, the delete function that takes the same additional parameters as the placement new operator. If no matching placement delete is found, no deallocation takes place. (See Chapter 3 for more information on new and delete expressions.)

Example 6-13 shows a silly class that allocates two dynamic arrays in its constructor. Suppose the first allocation fails (member str_). In that case, the compiler knows that str_ has not been initialized and so does not call the auto_ptr<> destructor, nor does the compiler call the destructor for the wstr_ member. If, however, the allocation for str_ succeeds and fails for wstr_, the compiler calls the auto_ptr<> destructor for str_ to free the memory that had been allocated successfully. Note that if auto_ptr<> were not used, the class would have a memory leak because the compiler would not be able to free the memory for str_. (Pointers don't have destructors.)

A destructor finalizes an object when the object is destroyed. Typically, finalization frees memory, releases resources (such as open files), and so on. A destructor is a special member function. It has no return type, no arguments, and cannot return a value. A destructor can be virtual, it can be inline, but it cannot be static, const, or volatile. The name of a destructor is a tilde (~) followed by the class name. A class has only one destructor (although it may have several constructors).

Just as constructors automatically construct base-class objects, a destructor automatically calls destructors for nonstatic data members, direct base classes, and virtual base classes after the derived-class destructor body returns. Member and base-class destructors are called in reverse order of their constructors. If you do not write a destructor, the compiler does so for you. (See the next section, Section 6.3.6.) Any class that has virtual functions should have a virtual destructor, as explained in Section 6.4 later in this chapter.

You can call a destructor explicitly, but there is rarely any reason to do so. The compiler calls destructors automatically when objects are destroyed. Dynamically allocated objects are destroyed by delete expressions. Static objects are destroyed when a program terminates. Other named objects are destroyed automatically when they go out of scope. Temporary, unnamed objects are destroyed automatically at the end of the expression that created them.

When you write a destructor, make sure that it never throws an exception. When an exception causes the stack to be unwound (see Chapter 5), objects are automatically destroyed. If a destructor throws an exception during stack unwinding, the program terminates immediately. (See <exception> in Chapter 13.)

Example 6-14 shows how a simple destructor is declared.

The compiler implicitly declares and defines default and copy constructors, the copy assignment operator, and the destructor in certain circumstances. All these implicit functions are inline public members. This section describes what each function does and the circumstances under which the function is implicitly declared and defined. Example 6-15 explicitly shows how each implicit member function is defined.

For classes whose data members have fundamental, class, or enumeration types, the implicit functions are often adequate. The most common case in which you must implement these functions explicitly is when an object manages pointers to memory that the object controls. In this case, a copy constructor or copy assignment operator must not blindly copy a member that is a pointer, which results in two pointers to the same memory. Instead, you should allocate a new pointer and copy the contents. In such cases, you will often find yourself providing the copy constructor, copy assignment operator, and destructor.

If you want to store objects in a standard container, you must make sure it has a copy constructor and a copy assignment operator (implicit or explicit). See Chapter 10 for information about the standard containers.