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.
Example 6-9. A pointer-to-member
#include <iostream> #include <ostream> class base { public: base(int i) : x_(i) {} virtual ~base( ) {} virtual void func( ) { std::cout << "base::func( )\n"; } private: int x_; }; class derived : public base { public: derived(int i) : base(i) {} virtual void func( ) { std::cout << "derived::func( )\n"; } }; int main( ) { base *b = new derived(42);void (base::*fp)( ) = &base::func;
(b->*fp)( );
// Prints derived::func( ) }
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
.
Example 6-10. The this keyword
class point { public: point(int x=0, int y=0) : x_(x), y_(y) {} int x( ) const { return this->x_; } int y( ) const { return y_; } void x(int x) { this->x_ = x; } void y(int y) { y_ = y; } bool operator==(const point& that) { return this->x( ) == that.x( ) && this->y( ) == that.y( ); } void move_to(context*) const { context->move_to(*this); } void draw_to(context*) const { context->draw_to(*this); } void get_position(context*) { context->getpos(this); } private: int x_, y_; };
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:
They do not have this
pointers.
They cannot be virtual
.
They cannot have const
or
volatile
qualifiers.
They cannot refer to nonstatic members, except as members of
a specific object (using the . or ->
operator).
A class cannot have a static and nonstatic member function with the same name and parameters.
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.
The order in which initializers appear in the initializer list is irrelevant. The order of initialization is determined by their declarations according to the following rules:
Virtual base classes are initialized first, in order of declaration in a depth-first traversal of the inheritance graph. Each virtual base class is initialized exactly once for the most-derived object.
Nonvirtual, direct base classes are initialized next, in order of declaration.
Nonstatic data members are initialized next, in order of declaration.
Finally, the body of the constructor is executed.
Note that each base class is constructed according to the same rules. This causes the root of the inheritance tree to be initialized first, and the most-derived class to be initialized last.
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.
Two kinds of constructors are special, so special they have their own names: default constructors and copy constructors .
A default constructor can be called with no arguments. It might be declared with no parameters, default arguments for every parameter, or an ellipsis as the sole parameter. (The last case is uncommon.) Default constructors are called when you construct an array of objects, when you omit the initializer for a scalar object, or when you use an empty initializer in an expression:
point corners[2]; point p; point *ptr = new point( );
(Remember not to use an empty initializer in a declaration.
For example, point
p( );
declares a function named p
, not a default-initialized point
object. See Chapter 2 for details.)
A copy constructor can take a single argument whose type is a
reference to the class type. (Additional parameters, if any, must
have default arguments.) The reference is usually const
, but it does not have to be. The
copy constructor is called to copy objects of the class type when
passing objects to functions and when returning objects from
functions.
The following example shows a default and a copy constructor:
struct point { point( ); // Default constructor point(const point& pt); // Copy constructor ...
Default and copy constructors are so important, the compiler might generate them for you. See Section 6.3.6 later in this chapter for details.
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:
With automatic variables and constants in a function body:
void demo( ) { point p1(1, 2); const point origin; ...
With static objects that are local or global:
point p2(3, 4); void demo( ) { static point p3(5, 6); ...
Dynamically, with new
expressions:
point *p = new point(7, 8);
With temporary objects in expressions:
set_bounds(point(left, top), point(right, bottom));
With function parameters and return values:
point offset(point p) { p.x(p.x( ) + 2); return p; } point x(1,2); x = offset(x); // Calls point(const point&) twice
With implicit type conversions:
point p = 2; // Invokes point(int=0, int=0), then point(const point&)
With constructor initializers:
derived::derived(int x, int y) : base(x, y) {}
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); } ...
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.
Example 6-12. Catching exceptions in constructors
struct array { // Initialize the array with all the items in the range (first, last). If an // exception is thrown while copying the items, be sure to clean up by // destroying the items that have been added so far. template<typename InIter> array(InIter first, InIter last) try : data_(0), size_(0) { for (InIter iter(first); iter != last; ++iter) push_back(*iter); } catch (...) { clear( ); } ... private: int* data_; std::size_t size_; };
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.
Example 6-14. Declaring a destructor
class string {
public:
string(std::size_t n, char c = '\0')
: str_(new char[n+1]), size_(n) {
std::fill(str_, str_+size_+1, c);
}
string( ) : str_(new char[1]), size_(0) {
str_[0] = '\0';
} ~string( ) { delete[] str_; }
const char* c_str( ) const { return str_; }
std::size_t size( ) const { return size_; }
private:
std::size_t size_;
char* str_;
};
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.
Example 6-15. Implicit member functions
// These classes show explicitly what the implicit member functions would be. class base { public: base( ) {} base(const base& that) : m1_(that.m1_), m2_(that.m2_) {} base& operator=(const base& that) { this->m1_ = that.m1_; this->m2_ = that.m2_; return *this; } ~base( ) {} private: int m1_; char* m2_; }; class demo { public: demo( ) {} // Default constructs three base objects demo(demo& d) {} // Copies three base objects in ary_[] demo& operator=(const demo& that) { this->ary_[0] = that.ary_[0]; this->ary_[1] = that.ary_[1]; this->ary_[2] = that.ary_[2]; return *this; } ~demo( ) {} // Default destructs three base objects private: base ary_[3]; }; class derived : public base { public: derived( ) : base( ) {} // Constructs m3_[] derived(derived& that) : base(that) {} // Copies m3_[] derived& operator=(const derived& that) { static_cast<base&>(*this) = static_cast<const base&>(that); this->m3_[0] = that.m3_[0]; this->m3_[1] = that.m3_[1]; this->m3_[2] = that.m3_[2]; return *this; } ~derived( ) {} // Calls ~base( ), destructs 3 demo objects private: demo m3_[3]; };
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.
A useful guideline is that if you write one of the three special functions (copy constructor, copy assignment operator, or destructor), you will probably need to write all three.
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.
The compiler implicitly declares a default constructor if a class has no user-defined constructors. The implicit default constructor calls the default constructor for all base classes and for all nonstatic data members of class type. Other nonstatic data members are left uninitialized. In other words, the behavior is the same as if you wrote the default constructor with no initializers and an empty function body.
The compiler implicitly declares a copy constructor if a class has no copy constructor.
If every direct base class and virtual base class has a copy
constructor that takes a const
reference parameter, and every nonstatic data member has a copy
constructor with a const
reference parameter, the implicit copy constructor also takes a
const
reference parameter.
Otherwise, the implicit copy constructor takes a plain reference
parameter.
In other words, the compiler tries to declare the implicit
copy constructor so it takes a const
reference parameter. If it cannot
because the implicit function would end up calling an inherited or
member copy constructor that does not take a const
parameter, the compiler gives up and
declares a copy constructor that takes a non-const
parameter.
An implicit copy constructor calls the copy constructor for each direct base class and each virtual base class and then performs a member-by-member copy of all nonstatic data members. It calls the copy constructor for each member of class type and copies the values for members of nonclass type.
The compiler implicitly declares a copy assignment operator (operator=
) if a class does not have one.
If every direct base class and virtual base class has a copy
assignment operator that takes a const
reference parameter, and if every
nonstatic data member has a copy assignment operator with a const
reference parameter, the implicit
copy assignment operator also takes a const
reference parameter. Otherwise, the
implicit copy assignment operator takes a plain reference
parameter.
In other words, the compiler tries to declare the implicit
copy assignment operator, so it takes a const
reference parameter. If it cannot
because the implicit function would end up calling an inherited or
member copy assignment operator that does not take a const
parameter, the compiler gives up and
declares a copy assignment operator that takes a non-const
parameter.
An implicit copy assignment operator calls the copy assignment operator for each direct base class and each virtual base class and then performs a member-by-member assignment of all nonstatic data members. It calls the copy assignment operator for each member of class type and assigns the values for members of nonclass type.