This Pointer
Unit 2 This Pointer, Friends, and Static Functions
Structure
2.1 Introduction
2.2 The this Pointer
2.3 Friends
2.3.1 Friend Functions
2.3.2 Friend Classes
2.3.3 Friend Scope
Self Assessment Questions
2.4 Static Functions
2.5 Summary
2.6 Terminal Questions
References and Further Reading
2.1 Introduction
This chapter with some of the very important and handy features of C++, such as, the this pointer, friend functions, and static functions.
Objectives
At the end of this chapter that contains certain miscellaneous yet important features of C++ you will be able to:
· Understand what this pointer is, and also the purpose of this pointer.
· Understand friends, its advantages, and scope considerations.
· Understand static functions.
· Use the features described in this unit in your programs.
2.2 The this Pointer
The this
pointer is used as a pointer to the class object instance by the member
function. The address of the class instance is passed as an implicit
parameter to the member functions. The sample below, in this section
shows how to use it. It is common knowledge that C++ keeps only one copy
of each member function and the data members are allocated memory for
all of their instances. This kind of various instances of data is
maintained using this pointer. Look at the sample below.
The following are the features of this pointer:
· this
pointer stores the address of the class instance, to enable pointer
access of the members to the member functions of the class.
· this pointer is not counted for calculating the size of the object.
· this pointers are not accessible for static member functions.
· this pointers are not modifiable.
Look at the following example to understand how to use the 'this' pointer:
class this_pointer_example { // class for explaining C++ tutorial
int data1;
public:
//Function using this pointer for C++ Tutorial
int getdata() {
return this->data1;
}
//Function without using this pointer
void setdata(int newval) {
data1 = newval;
}
};
Thus, a member function can gain the access of data member by either using this pointer or not.
2.3 Friends
Friend
classes in C++ give us access to non-member functions or other classes.
By using the friend keyword, a class can grant access to non-member
functions or to another class. These friend functions and friend classes
are permitted to access private and protected class members. There are
places where friends can lead to more intuitive code, and are often
needed to correctly implement operator overloading.
If
encountering friend functions for the first time, you might feel
slightly uneasy since they seem to violate encapsulation. This feeling
may stem from the fact that a friend function is not strictly a member
of the class. By thinking of a friend function as part of the class’s
public interface, you can get a better understanding of how friends
work. From a design perspective, friends can be treated in a similar way
to public member functions. The concept of a class interface can be
extended from public members to include friend functions and friend
classes. Put another way: Friend functions do not break encapsulation;
instead they naturally extend the encapsulation barrier.
2.3.1 Friend Functions
Friend
functions can be declared anywhere within a class declaration, but it
is common practice to list friends at the beginning of the class. The
public and protected keywords do not apply to friend functions, as the
class has no control over the scope of friends.
If we want to
declare an external function as friend of a class, thus allowing this
function to have access to the private and protected members of this
class, we do it by declaring a prototype of this external function
within the class, and preceding it with the keyword friend:
// friend functions
#include <iostream>
using namespace std;
class CRectangle {
int width, height;
public:
void set_values (int, int);
int area () {return (width * height);}
friend CRectangle duplicate (CRectangle);
};
void CRectangle::set_values (int a, int b) {
width = a;
height = b;
}
CRectangle duplicate (CRectangle rectparam) {
CRectangle rectres;
rectres.width = rectparam.width*2;
rectres.height = rectparam.height*2;
return (rectres);
}
int main () {
CRectangle rect, rectb;
rect.set_values (2,3);
rectb = duplicate (rect);
cout << rectb.area();
return 0;
}
The
duplicate function is a friend of CRectangle. From within that function
we have been able to access the members width and height of different
objects of type CRectangle, which are private members. Notice that
neither in the declaration of duplicate() nor in its later use in main()
have we considered duplicate a member of class CRectangle. It isn't! It
simply has access to its private and protected members without being a
member.
The friend functions can serve, for example, to conduct
operations between two different classes. Generally, the use of friend
functions is out of an object-oriented programming methodology, so
whenever possible it is better to use members of the same class to
perform operations with them. Such as in the previous example, it would
have been shorter to integrate duplicate() within the class CRectangle.
2.3.2 Friend Classes
As
well as choosing a non-member friend function, a class has two other
possibilities for its friends. A class can declare a member function of
another class as a friend, or declare another class as a friend class.
Friend
classes are used in cases where one class is tightly coupled to another
class. For example, suppose we have a class CPoint that represents a
coordinate, and a class CPointCollection that holds a list of points.
Since the collection class may need to manipulate point objects, we
could declare CPointCollection as a friend of the CPoint class:
// Forward declaration of friend class.
class CPointCollection;
// Point class.
class CPoint {
friend CPointCollection;
private:
double m_x;
double m_y;
public:
CPoint(const double x, const double y) :
m_x(x),
m_y(y) { }
~CPoint(void) { }
// ...
};
Since
the collection class is a friend of CPoint, it has access to the
internal data of any point object. This is useful when individual
elements of the collection need to be manipulated. For example, a set
method of the CPointCollection class could set all CPoint elements to a
particular value (vector is a STL container which is discussed in detail
in Chapter 8):
class CPointCollection {
private:
vector<CPoint> m_vecPoints;
public:
CPointCollection(const int nSize) :
m_vecPoints(nSize) { }
~CPointCollection(void);
void set(const double x, const double y);
// ...
};
The set member can iterate over the collection and reset each point:
void CPointCollection::set(const double x, const double y) {
// Get the number of elements in the collection.
const int nElements = m_vecPoints.size();
// Set each element.
for(int i=0; i<nElements; i++) {
m_vecPoints[i].m_x = x;
m_vecPoints[i].m_y = y;
}
}
One
thing worth noting about friend classes is that friendship is not
mutual: although CPointCollection can access CPoint, the converse is not
true. Friendship is also not something that is passed down a class
hierarchy. Derived classes of CPointCollection will not be able to
access CPoint. The principle behind this is that friendship is not
implicitly granted; each class must explicitly choose its friends.
2.3.3 Friend Scope
The
name of a friend function or class first introduced in a friend
declaration is not in the scope of the class granting friendship (also
called the enclosing class) and is not a member of the class granting friendship.
The
name of a function first introduced in a friend declaration is in the
scope of the first nonclass scope that contains the enclosing class. The
body of a function provided in a friend declaration is handled in the
same way as a member function defined within a class. Processing of the
definition does not start until the end of the outermost enclosing
class. In addition, unqualified names in the body of the function
definition are searched for starting from the class containing the
function definition.
If the name of a friend class has been
introduced before the friend declaration, the compiler searches for a
class name that matches the name of the friend class beginning at the
scope of the friend declaration. If the declaration of a nested class is
followed by the declaration of a friend class with the same name, the
nested class is a friend of the enclosing class.
The scope of a friend class name is the first nonclass enclosing scope. For example:
class A {
class B { // arbitrary nested class definitions
friend class C;
};
};
is equivalent to:
class C;
class A {
class B { // arbitrary nested class definitions
friend class C;
};
};
If the friend function is a member of another class, you need to use the scope resolution operator (::). For example:
class A {
public:
int f() { }
};
class B {
friend int A::f();
};
Friends of a base class are not inherited by any classes derived from that base class. The following example demonstrates this:
class A {
friend class B;
int a;
};
class B { };
class C : public B {
void f(A* p) {
//p->a = 2;
}
};
The compiler would not allow the statement p->a = 2 because class C is not a friend of class A, although C inherits from a friend of A.
Friendship is not transitive. The following example demonstrates this:
class A {
friend class B;
int a;
};
class B {
friend class C;
};
class C {
void f(A* p) {
//p->a = 2;
}
};
The compiler would not allow the statement p->a = 2 because class C is not a friend of class A, although C is a friend of a friend of A.
If
you declare a friend in a local class, and the friend's name is
unqualified, the compiler will look for the name only within the
innermost enclosing nonclass scope. You must declare a function before
declaring it as a friend of a local scope. You do not have to do so with
classes. However, a declaration of a friend class will hide a class in
an enclosing scope with the same name. The following example
demonstrates this:
class X { };
void a();
void f() {
class Y { };
void b();
class A {
friend class X;
friend class Y;
friend class Z;
//friend void a();
friend void b();
//friend void c();
};
::X moocow;
//X moocow2;
}
In the above example, the compiler will allow the following statements:
· friend class X: This statement does not declare ::X as a friend of A, but the local class X as a friend, even though this class is not otherwise declared.
· friend class Y: Local class Y has been declared in the scope of f().
· friend class Z: This statement declares the local class Z as a friend of A even though Z is not otherwise declared.
· friend void b(): Function b() has been declared in the scope of f().
· ::X moocow: This declaration creates an object of the nonlocal class ::X.
The compiler would not allow the following statements:
· friend void a(): This statement does not consider function a() declared in namespace scope. Since function a() has not been declared in the scope of f(), the compiler would not allow this statement.
· friend void c(): Since function c() has not been declared in the scope of f(), the compiler would not allow this statement.
· X moocow2: This declaration tries to create an object of the local class X, not the nonlocal class ::X. Since local class X has not been defined, the compiler would not allow this statement.
Self Assessment Questions
1. Name the types of friends that can be found in C++.
2. Implement the examples provided in this section and observe the results.
2.4 Static Functions
Static data types can be accessed without instantiation of the class in C++. This is applicable for static functions also.
The differences between a static member function and non-static member functions are as follows:
·
A static member function can access only static member data, static
member functions and data and functions outside the class. A non-static
member function can access all of the above including the static data
member.
· A static member function can be called, even when a
class is not instantiated, a non-static member function can be called
only after instantiating the class as an object.
· A static member function cannot be declared virtual, whereas a non-static member functions can be declared as virtual
· A static member function cannot have access to the 'this' pointer of the class.
·
The static member functions are not used very frequently in programs.
But nevertheless, they become useful whenever we need to have functions
which are accessible even when the class is not instantiated.
You cannot have static and non-static member functions with the same names and the same number and type of arguments.
Like static data members, you may access a static member function f() of a class A without using an object of class A.
A static member function does not have a this pointer. The following example demonstrates this:
#include <iostream>
using namespace std;
struct X {
private:
int i;
static int si;
public:
void set_i(int arg) { i = arg; }
static void set_si(int arg) { si = arg; }
void print_i() {
cout << "Value of i = " << i << endl;
cout << "Again, value of i = " << this->i << endl;
}
static void print_si() {
cout << "Value of si = " << si << endl;
//cout << "Again, value of si = " << this->si << endl;
}
};
int X::si = 77; //Initialize static data member
int main() {
X xobj;
xobj.set_i(11);
xobj.print_i();
//static data members and functions belong to the class and
//can be accessed without using an object of class X
X::print_si();
X::set_si(22);
X::print_si();
}
Output:
Value of i = 11
Again, value of i = 11
Value of si = 77
Value of si = 22
The compiler does not allow the member access operation this->si in function A::print_si() because this member function has been declared as static, and therefore does not have a this pointer.
You can call a static member function using this pointer of a non-static member function. In the following example, the non-static member function printall() calls the static member function f() using this pointer:
#include <iostream>
using namespace std;
class C {
static void f() {
cout << "Here is i: " << i << endl;
}
static int i;
int j;
public:
C(int firstj): j(firstj) { }
void printall();
};
void C::printall() {
cout << "Here is j: " << this->j << endl;
this->f();
}
int C::i = 3;
int main() {
C obj_C(0);
obj_C.printall();
}
Output:
Here is j: 0
Here is i: 3
A static member function cannot be declared with the keywords virtual, const, volatile, or const volatile.
A
static member function can access only the names of static members,
enumerators, and nested types of the class in which it is declared.
Suppose a static member function f() is a member of class X. The static member function f() cannot access the non-static members X or the non-static members of a base class of X.
2.5 Summary
Friend
functions and friend classes allow a class interface to be extended in a
natural and efficient way. For some problems, friend functions lead to
more intuitive code, while friend classes are needed when two classes
are tightly coupled. Non-member friend functions are especially useful
for implementing operator overloading, a good example being
operator<<.
Friend functions do not break encapsulation,
but rather enhance a class interface. Although this is the case, friends
should be used judiciously; a member function will often do just as
well. As a general rule of thumb, use member functions where possible,
and friends where necessary. A well-designed project will not be free of
friend functions, but will instead use them in places where non-members
are naturally part of a class.
This chapter also discussed the use of this pointer. This chapter has also discussed static functions.
2.6 Terminal Questions
1. What is this pointer?
2. How is this pointer helpful?
3. Describe friend functions and friend classes.
4. Outline the differences between static member functions and non-static member functions.
5. A static member function does not have a this pointer. Is the previous sentence true or false?
6. Point out the keywords that may not be suffixed with static member functions.
No comments:
Post a Comment