2012年2月16日 星期四

Friend Functions


A friend function is a non-member function that has access to the private parts of a class. Friendship can be granted in three ways:
  • to an independent (non-class member) function
  • to a class member function of another class
  • to another class (to all functions in that class)

A friend function is always a non-class member. A function outside of a class cannot "seek friendship". Friendship is only granted by a class to another function (or class). A friend has access to all private members.

It is common practice for friend functions to have arguments which include references to the (friendship-granting) class.

Example 5-5 - An independent friend

1      // File: ex5-5.cpp
2     
3      #include <iostream>
4      using namespace std;
5     
6      class circle
7      {
8       friend void print(const circle&);      // name a friend of the class
9       public:
10         circle (double r) { radius = r;}
11      private:
12        double radius;
13      };
14     
15      int main(void)
16      {
17        circle c1(5.);
18        print(c1);                   // prints This circle has radius 5
19        circle c2(1.);
20        print(c2);                   // prints This circle has radius 1
21        return 0;
22      }
23     
24      void print(const circle& c)
25      {
26        cout << "This circle has radius " << c.radius << endl;
27      }


Friendly advice

  • Friend functions are not affected by their location in a class definition or any access specifiers.
  • Granting friendship to another function or class is not reciprocal. If class xyz declares that class abc is a friend, then class xyz is not necessarily a friend to class abc.
  • Friendship is not transitive. If class xyz grants friendship to class abc, and class abc grants friendship to class def, then the friendship from xyz is not automatically granted to def.
  • Friendship is not inherited. The friend of a base class is not a friend to a class derived from the base. Further, if a base class, B, is a friend to another class, C, classes derived from B are not friends of C.  (My friends are not necessarily my children's friends, and my children's friends are not my friends.)
  • Class member functions operate on the object that invokes the function. Friend functions operate on objects that are passed as arguments.

Granting friendship to another class
  • If class dog grants friendship to class cat, then any function of the cat class can access any member of the dog class.
  • The word class is optional in the grant of friendship to another class.

Granting friendship to a function of another class
  • To grant friendship to a member of another class, you must indicate the class name and function name using the scope resolution operator.
  • If you want the dog class to grant friendship to the meow function of the cat class, you must:
  1. forward declare the dog class.
  2. define the cat class, declaring the meow function, but not defining it.
  3. define the dog class, identifying the friend function, cat::meow().
  4. define the cat member functions.
Example 5-6 - A friend to the card and deck classes

1      // File: ex5-6.cpp - a friend to the card and deck classes
2     
3      #include <iostream>
4      #include <stdlib>            // needed for rand() function
5      using namespace std;
6     
7      const char* value_name[13] = {"two","three","four","five","six",
8          "seven","eight","nine","ten","jack","queen","king","ace"};
9     
10      const char* suit_name[4] = {"clubs","diamonds","hearts","spades"};
11     
12      const int HandSize = 5;
13      const int DeckSize = 52;
14     
15      class deck;                // What’s this?
16     
17      class hand
18      {
19        private:
20             int card_no[HandSize];
21        public:
22            hand() { }
23            void dealMe(deck&);
24            void print(const deck&) const;
25      };
26     
27      class card
28      {
29        private:
30            int value;
31            int suit;
32        public:
33            card() { }
34            void assign(int);
35            int get_value(void) const { return value;}
36            int get_suit(void) const { return suit;}
37            void print(void) const;
38        friend void hand::print(const deck&) const;
39      };
40     
41      void card::assign(int x)        // why not use the ctor?
42      {
43        value = x % 13;
44        suit = x % 4;
45      }
46     
47      void card::print(void) const
48      {
49        cout << value_name[value] << " of " << suit_name[suit] << endl;
50      }
51      class deck {
52        friend class hand;
53        private:
54           card d[DeckSize];
55           int next_card;
56        public:
57           deck(void);
58           void shuffle(void);
59           void deal(int=HandSize);
60           void print(void) const;
61      };
62     
63      deck::deck(void)
64      {
65        for (int i = 0; i < DeckSize; i++) d[i].assign(i);
66        next_card = 0;
67      }
68     
69      void deck::shuffle(void)
70      {
71        int i, k;
72        card temp;
73        cout << "I am shuffling the deck\n";
74        for (i = 0; i < DeckSize; i++)
75        {
76           k = rand() % DeckSize;
77           temp = d[i];
78           d[i] = d[k];
79           d[k] = temp;
80        }
81      }
82     
83      void deck::print(void) const
84      {
85        int i;
86        for (i = 0; i < DeckSize; i++) d[i].print();
87      }
88     
89      void hand::dealMe(deck& dk)        // why isn’t dk const deck& ?
90      {
91        int i;
92        for (i = 0; i < HandSize; i++) card_no[i] = dk.next_card++;
93        return;
94      }
95     
96      void hand::print(const deck& dk) const
97      {
98        int i;
99        cout << "here is your hand:\n";
100        for (i = 0; i < HandSize; i++) dk.d[card_no[i]].print();
101      }
102      int main (void) {
103        deck poker;
104        poker.shuffle();
105        poker.print();
106        hand Joe;
107        hand Mary;
108        Joe.dealMe(poker);
109        Mary.dealMe(poker);
110        cout << "\nOk, Joe ";
111        Joe.print(poker);
112        cout << "\nOk, Mary ";
113        Mary.print(poker);
114        return 0;
115      }


******  Output  ******

I am shuffling the deck
ten of hearts
ace of diamonds
queen of hearts
three of hearts
four of diamonds
eight of diamonds
eight of spades
eight of hearts
seven of spades
six of hearts
.
.
.

Ok, Joe here is your hand:
ten of hearts
ace of diamonds
queen of hearts
three of hearts
four of diamonds

Ok, Mary here is your hand:
eight of diamonds
eight of spades
eight of hearts
seven of spades
six of hearts

Does hand::print() have to be declared as a friend of the card class?

How can you change the code to eliminate all friend functions?

Example 5-7 - More friendly poker

1      // File: ex5-7.cpp
2     
3      #include <iostream>
4      #include <cstdlib>            // needed for rand() function
5      #include <cstring>
6      #include <cassert>
7      using namespace std;
8     
9      const int HandSize = 5;
10      const int DeckSize = 52;
11      const char* value_name[13] = {"two","three","four","five","six",
12          "seven","eight","nine","ten","jack","queen","king","ace"};
13      const char* suit_name[4] = {"clubs","diamonds","hearts","spades"};
14     
15      class deck;
16     
17      class hand
18      {
19        friend void threeOrFourOfAKind(const deck& d,const hand&);
20        private:
21           char* name;
22           int card_no[5];
23        public:
24           hand(void);
25           ~hand();
26           void dealMe(deck&);
27           void print(const deck&) const;
28           char* getName(void) {return name;}
29      };
30     
31      hand::hand(void) {
32        char temp[32];
33        cout << "Enter player name => ";
34        cin >> temp;
35        name = new char[strlen(temp) + 1];
36        strcpy(name,temp);
37      }
38     
39      hand::~hand() {
40        delete [] name;
41      }
42     
43      class card
44      {
45        private:
46           int value;
47           int suit;
48        public:
49           card () { }
50           void assign(int);
51           int get_value(void) const { return value;}
52           int get_suit(void) const { return suit;}
53           void print(void) const;
54      };
55      void card::assign(int x) {
56        value = x % 13;
57        suit = x % 4;
58      }
59     
60      void card::print(void) const {
61        cout << value_name[value] << " of " << suit_name[suit] << endl;
62      }
63     
64      class deck
65      {
66        friend void threeOrFourOfAKind(const deck&,const hand&);
67        friend class hand;
68        private:
69           card d[DeckSize];
70           int next_card;
71           void shuffle(void);
72        public:
73           deck();
74           void print(void) const;
75      };
76     
77      deck::deck() {
78        int i;
79        for (i = 0; i < DeckSize; i++) d[i].assign(i);
80        next_card = 0;
81        shuffle();
82      }
83     
84      void deck::shuffle(void) {
85        int i,k;
86        card temp;
87        cout << "I am shuffling the deck\n";
88        for (i = 0; i < DeckSize; i++)
89        {
90           k = rand() % DeckSize;
91           temp = d[i];
92           d[i] = d[k];
93           d[k] = temp;
94        }
95      }
96     
97      void deck::print(void) const {
98        int i;
99        for (i = 0; i < DeckSize; i++) d[i].print();
100      }
101     
102      void hand::dealMe(deck& dk) {
103        int i;
104        assert(dk.next_card < DeckSize-4);
105        for (i = 0; i < HandSize; i++) card_no[i] = dk.next_card++;
106      }
107     
108      void hand::print(const deck& dk) const {
109        int i;
110        for (i = 0; i < HandSize; i++) dk.d[card_no[i]].print();
111      }
112     
113      int main (void) {
114        deck poker;
115     
116        hand player[3];
117        int i;
118        for (i = 0; i < 3; i++) player[i].dealMe(poker);
119        for (i = 0; i < 3; i++)
120        {
121          cout << "\nOk " << (player[i].getName())
122               << ", here is your hand\n";
123             player[i].print(poker);
124             threeOrFourOfAKind(poker,player[i]);
125        }
126        return 0;
127      }
128     
129      void threeOrFourOfAKind(const deck& dk,const hand& who) {
130        int temp;
131        int card_count;
132        for (int i = 0; i < 3; i++)
133        {
134          card_count = 1;
135             temp = (dk.d[who.card_no[i]]).get_value();
136             for (int j = i + 1; j < HandSize; j++)
137              if (temp==dk.d[who.card_no[j]].get_value()) card_count++;
138             if (card_count > 2)
139             {
140            cout << "Hey, " << (who.getName()) << ", you have "
141                 <<card_count << ' ' << value_name[temp] << "s.\n";
142               return;
143             }
144        }
145        return;
146      }


****** Sample Run ******

I am shuffling the deck
Enter player name => Joe
Enter player name => Mabel
Enter player name => Bob
Ok Joe, here is your hand
ten of hearts
ace of diamonds
queen of hearts
three of hearts
four of diamonds

Ok Mabel, here is your hand
eight of diamonds
eight of spades
eight of hearts
seven of spades
six of hearts
Hey, Mabel, you have 3 eights.

Ok Bob, here is your hand
five of clubs
jack of clubs
jack of spades
ace of hearts
king of clubs

沒有留言:

張貼留言