Tomomoto
Classes can not only have friend functions, but also classes can be used as friends. In this case, all methods of the friend class can access the private and protected members of the original class. So although friends are granted access to the private part of the class from the outside, they do not contradict the idea of object-oriented programming, on the contrary, they improve the flexibility of the common interface.
friend class
Suppose you need to write a simple program that simulates a TV and a remote control, and define two classes Tv and Remote. The remote control can change the state of the TV, which indicates that the Remote class is a friend of the Tv class
First define the Tv class. A TV can be represented by a set of state members (variables that describe various aspects of the TV):
- switch
- channel settings
- volume setting
- CATV or Antenna Tuning Mode
- TV coordinator or A/V input
The following statement makes Remote a friend class:
friend class Remote;
Friend declarations can be in public, private or protected sections, it doesn't matter where they are located.
tv.h
#ifndef TV_H_
#define TV_H_
class Tv
{
public:
friend class Remote; //Remote can access Tv private parts
enum{Off,On};
enum{MinVal,MaxVal=20};
enum{Antenna,Cable};
enum{TV,DVD};
Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc),channel(2),mode(Cable),input(TV){}
void onoff() { state = (state == On ? Off : On); }
bool ison()const { return state == On; }
bool volup();
bool voldown();
void chanup();
void chandown();
void set_mode() { mode = (mode == Antenna) ? Cable : Antenna; }
void set_input() { input = (input == TV) ? DVD : TV; }
void settings()const; //display all settings
private:
int state; //on or off
int volume; //assumed to be digitized
int maxchannel; //maximum number of channels
int channel; //current channel setting
int mode; //broadcast or cable
int input; //TV or DVD
};
class Remote
{
private:
int mode;
public:
Remote(int m=Tv::TV):mode(m){}
bool volup(Tv &t) { return t.volup(); }
bool voldown(Tv& t) { return t.voldown(); }
void onoff(Tv& t) { t.onoff(); }
void chanup(Tv& t) { t.chanup(); }
void chandown(Tv& t) { t.chandown(); }
void set_chan(Tv& t, int c) { t.channel = c; }
void set_mode(Tv& t) { t.set_mode(); }
void set_input(Tv& t) { t.set_input(); }
};
#endif // !TV_H_
Except for the constructor, all Remote methods take a Tv object reference as a parameter, which indicates that the remote must be specific to the TV
tv.cpp
#include<iostream>
#include"tv.h"
bool Tv::volup()
{
if (volume < MaxVal)
{
volume++;
return true;
}
else
return false;
}
bool Tv::voldown()
{
if (volume > MinVal)
{
volume--;
return true;
}
else
return false;
}
void Tv::chanup()
{
if (channel < maxchannel)
{
channel++;
}
else
channel=1;
}
void Tv::chandown()
{
if (channel > 1)
{
channel--;
}
else
channel = maxchannel;
}
void Tv::settings()const
{
using std::cout;
using std::endl;
cout << "TV is " << (state == Off ? "Off" : "On") << endl;
if (state == On)
{
cout << "Volume setting = " << volume << endl;
cout << "Channel setting = " << channel << endl;
cout << "Mode = " << (mode == Antenna ? "antenna" : "cable") << endl;
cout << "Input = " << (input == TV ? "TV" : "DVD") << endl;
}
}
use_tv.cpp
#include<iostream>
#include"tv.h"
int main()
{
using std::cout;
Tv s42;
cout << "Initial setting for 42\"TV:\n";
s42.settings();
s42.onoff();
s42.chanup();
cout << "\nAdjusted setting for 42\"TV:\n";
s42.settings();
Remote grey;
grey.set_chan(s42, 10);
grey.volup(s42);
grey.volup(s42);
cout << "\n42\"setting after using remote:\n";
s42.settings();
Tv s58(Tv::On);
s58.set_mode();
grey.set_chan(s58, 28);
cout<<"\n58\"setting:\n";
s58.settings();
return 0;
}
friend member function
In the code of the previous example, most of the Remote methods are implemented using the public interface of the Tv class. This means that these methods don't really need to be friends. In fact, the only Remote method that directly accesses the Tv member is Remote::set_chan(), so it is the only method that needs to be a friend. You can indeed choose to make only specific class members friends of another class, rather than making the entire class a friend.
But to do so one must be careful about the order of the various declarations and definitions .
The way to make Remote::set_chan() a friend of the Tv class is to declare it as a friend in the Tv class declaration
class Tv
{
friend void Remote::set_cahn(Tv& t, int c);
...
};
For the compiler to be able to process this statement, it must know the definition of Remote. Otherwise it cannot know that Remote is a class and set_chan is a method of this class. This means that the definition of Remote should be placed before the definition of Tv. The method of Remote mentions the Tv object, which means that the definition of Tv should come before the definition of Remote. The way to avoid this circular dependency is - use forward declaration
class Tv; //forward declaration
class Remote {...};
class Tv {...};
RTTI (Runtime Type Identification, runtime type identification)
C++ has 3 elements that support RTTI. (RTTI only applies to classes containing virtual functions)
- The dynamic_cast operator will use a pointer to the base class to produce a pointer to the derived class if possible; otherwise the operator returns 0 - a null pointer
- The typeid operator returns a value indicating the type of the object
- The type_info structure stores information about a particular type