Design Pattern Exercises in C++

I have to give credit to JLBorges for inspiring me into practising a lot of design patterns in my programming. As far as I know, there is no design patterns exercise book for C++, so here I will post my own exercises with solutions that I've written out with passion. All solutions were compiled with GCC 4.9, so the accuracy of the solution is there, but if anyone still has concerns or problems with my solutions, let me know. I can always stop posting these exercises, but I think having these exercises online is better than not having any at all, due to the void of such C++ exercises anywhere.

First exercise:
Many video stores have categories of movies-such as New Release (high price), General Release (standard price), Classic (low price), etc.... If the store wanted to raise the price on all New Release rentals from $5.00 to $6.50, it would have to iterate through all of the New Release movies and raise their rental price. That is not very convenient. Also, we want to decouple the concept of VideoTape and Movie so that the renter of the video tape can be a data member of VideoTape, and the movie's title and rental price can be data members of Movie, and then the two can vary independently (keep in mind that VideoTape is just one possible fomat--there can be DVD, BluRay, etc... and the whole hierarchy can be a real mess if even possible. What would be a suitable choice of design pattern to handle this situation?
Type Object Pattern:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <string>
#include <list>

class Person;

class MovieCategory {  // Type Class
	private:
		std::string categoryName;
		double rentalPrice;
	public:
		MovieCategory (const std::string& c, double p) : categoryName(c), rentalPrice(p) {}
		double getRentalPrice() const {return rentalPrice;}
};

class Movie {  // Type Class
	private:
		MovieCategory* category = nullptr;
		std::string title;
	public:
		Movie (MovieCategory* m, const std::string& t) : category(m), title(t) {}
		std::string getTitle() const {return title;}
		double getRentalPrice() const {return category->getRentalPrice();}
		void changeCategory (MovieCategory* c) {category = c;}
};

class VideoTape {  // Class.  Represents instances of Type Class.
	private:
		Movie* rentedMovie;
		Person* renter = nullptr;
	public:
		VideoTape (Movie* movie) : rentedMovie (movie) {}
		Movie* getRentedMovie() const {return rentedMovie;}
		std::string getTitle() const {return rentedMovie->getTitle();}
		double getRentalPrice() const {return rentedMovie->getRentalPrice();}
		Person* getRenter() const {return renter;}
		bool isRented() const {return renter != nullptr;}
};

int main() {
	MovieCategory *newRelease = new MovieCategory ("New Releases", 5.75), *general = new MovieCategory ("General", 3.25);
	Movie* starWarsMovie = new Movie (newRelease, "Star Wars");
	std::list<VideoTape*> videoTapes;
	for (int i = 0; i < 6; i++)
		videoTapes.emplace_back (new VideoTape (starWarsMovie));
	for (VideoTape* x: videoTapes)
		std::cout << "Video tape with title " << x->getTitle() << " and rental price $" << x->getRentalPrice() << " is on the shelf." << std::endl;

	std::cout << std::endl << "Star Wars is no longer a new release and is moved to category General." << std::endl << std::endl;
	starWarsMovie->changeCategory (general);   // Only one line is needed to change the price of the videos instead of individually changing the price of each video tape.
	for (VideoTape* x: videoTapes)
		std::cout << "Video tape with title " << x->getTitle() << " and has lowered its rental price to $" << x->getRentalPrice() << "." << std::endl;
}
/*
Output:

Video tape with title Star Wars and rental price $5.75 is on the shelf.
Video tape with title Star Wars and rental price $5.75 is on the shelf.
Video tape with title Star Wars and rental price $5.75 is on the shelf.
Video tape with title Star Wars and rental price $5.75 is on the shelf.
Video tape with title Star Wars and rental price $5.75 is on the shelf.
Video tape with title Star Wars and rental price $5.75 is on the shelf.

Star Wars is no longer a new release and is moved to category General.

Video tape with title Star Wars and rental price $3.25 is on the shelf.
Video tape with title Star Wars and rental price $3.25 is on the shelf.
Video tape with title Star Wars and rental price $3.25 is on the shelf.
Video tape with title Star Wars and rental price $3.25 is on the shelf.
Video tape with title Star Wars and rental price $3.25 is on the shelf.
Video tape with title Star Wars and rental price $3.25 is on the shelf.
*/
The system could also support DVDs and other formats. Movie may turn out to be a specific example of a more general WatchableItem class. WatchableItem might have subclasses like Movie, Documentary, and HowTo. Movies have ratings whereas Documentary and HowTo videos often do not.
HowTo videos often come in a series or collection that is rented all at once whereas Movies and Documentarys do not. Extend the above Object Pattern to handle these added details. Furthermore, find a suitable design pattern to be used within this to handle the fact that when a series is rented, the entire series (and not just individual parts) are to be rented, and write a play() function in this design pattern so that it is ensured that each part is played, and not one of the parts.
Using the Composite Pattern for the series rental is a good choice. The extended Type Object Pattern can be written as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <string>
#include <list>
#include <algorithm>

class Person;

class WatchableCategory {  // MovieCategory renamed to WatchableCategory
	private:
		std::string categoryName;
		double rentalPrice;
	public:
		WatchableCategory (const std::string& c, double p) : categoryName(c), rentalPrice(p) {}
		double getRentalPrice() const {return rentalPrice;}
};

class WatchableItem {
	private:
		WatchableCategory* category = nullptr;
		std::string title;
	public:
		WatchableItem (WatchableCategory* c, const std::string& t) : category(c), title(t) {}
		virtual ~WatchableItem() = default;
		std::string getTitle() const {return title;}
		double getRentalPrice() const {return category->getRentalPrice();}
		void changeCategory (WatchableCategory* c) {category = c;}
		virtual void play() const {std::cout << title << " being played at home." << std::endl;}
};

enum Genre {Action, Romance, Horror, Comedy, SciFi};
enum Rating {PG, AA, R};

class Movie : public WatchableItem {
	private:
		Genre genre;
	public:
		Movie (WatchableCategory* category, const std::string& title, Genre g) : WatchableItem (category, title), genre (g) {}
};

class RatedMovie : public Movie {
	private:
		Rating rating;
	public:
		RatedMovie (WatchableCategory* category, const std::string& title, Genre movieGenre, Rating rate) : Movie (category, title, movieGenre), rating (rate) {}	
};

class Documentary : public WatchableItem {
	public:
		using WatchableItem::WatchableItem;
	// ...
};

class HowTo : public WatchableItem {
	public:
		using WatchableItem::WatchableItem;
	// ...
};

class WatchableComponent : public WatchableItem {  // Component of Composite Pattern
	protected:
		WatchableComponent (WatchableCategory* category, const std::string& seriesTitle) : WatchableItem (category, seriesTitle) {}
	public:
		virtual void play() const override = 0;
};

class PartOfSeries : public WatchableComponent {  // Leaf of Composite Pattern
	private:
		int partNumber;
		std::string partTitle;
	public:
		PartOfSeries (WatchableCategory* category, const std::string& seriesTitle, int num, const std::string& title) : WatchableComponent (category, seriesTitle), partNumber (num), partTitle (title) {}
		virtual void play() const override {std::cout << "\"" << partTitle << "\", part " << partNumber << " of the series \"" << getTitle() << "\", being played." << std::endl;}
};

class ShowSeries : public WatchableComponent {  // Composite of Composite Pattern
	private:
		std::list<WatchableComponent*> parts;  // Part I, Part II, Part III, etc... of the series.
	public:
		ShowSeries (WatchableCategory* category, const std::string& seriesTitle) : WatchableComponent (category, seriesTitle) {}
		void addPart (WatchableComponent* component) {parts.emplace_back (component);}
		virtual void play() const override {for (WatchableComponent* x : parts) x->play();}
};

class RentableItem {
	private:
		WatchableItem* show;  // This is the only change that we need to made due to all the changes in the WatchableItem part of the code, generalizing Movie* to WatchableItem* (even if we don't make this change, we still have the same functionality as before, but we want to be able to rent documentaries and how-to-series).
		Person* renter = nullptr;
	protected:
		RentableItem (WatchableItem* movie) : show (movie) {}
	public:
		WatchableItem* getShow() const {return show;}
		std::string getTitle() const {return show->getTitle();}
		double getRentalPrice() const {return show->getRentalPrice();}
		Person* getRenter() const {return renter;}
		void setRenter (Person* person) {renter = person;}
		bool isRented() const {return renter != nullptr;}
		void play() const {show->play();}
		virtual void print() const = 0;	
};

class VideoTape : public RentableItem {  // No changes need to be made here!
	private:
		bool rewound;
	public:
		VideoTape (WatchableItem* movie, bool r = true) : RentableItem (movie), rewound (r) {}
		bool isRewound() const {return rewound;}
		void playTape() {std::cout << getTitle() << " is played." << std::endl;  rewound = false;}
		void rewindTape() {rewound = true;}
	private:
		virtual void print() const override {std::cout << "Video tape with title \"" << getTitle() << "\" and rental price $" << getRentalPrice() << " is on the shelf." << std::endl;}
};

class DVD : public RentableItem {  // No changes need to be made here!
	private:
		bool numDisks;
	public:
		DVD (WatchableItem* movie, int num = 1) : RentableItem (movie), numDisks (num) {}
		int getNumDisks() const {return numDisks;}
	private:
		virtual void print() const override {std::cout << "DVD with title \"" << getTitle() << "\" and rental price $" << getRentalPrice() << " is on the shelf." << std::endl;}
};

class Person {  // Finally defining Person so as to actually rent, play, and return a video.
	private:
		std::string name;
		double pocketChange;
	public:
		Person (const std::string& n, int change) : name(n), pocketChange (change) {}
		void rentItem (RentableItem* item, std::list<RentableItem*>& inventory) {
			std::cout << std::endl << "Before renting \"" << item->getTitle() << "\", " << name << " has $" << pocketChange << " (inventory size = " << inventory.size() << ")." << std::endl;
			item->setRenter (this);
			pocketChange -= item->getRentalPrice();
			inventory.remove (item);
			std::cout << std::endl << "After renting \"" << item->getTitle() << "\", " << name << " has $" << pocketChange << " (inventory size = " << inventory.size() << ")." << std::endl;
		}
		void play (const RentableItem* item) const {item->play();}  // Using the PartOfSeries::play() function of the Composite Pattern.
		void returnItem (RentableItem* item, std::list<RentableItem*>& inventory) {
			item->setRenter (nullptr);
			inventory.emplace_back (item);
			std::cout << name << " returns " << "\"" << item->getTitle() << "\" (inventory size = " << inventory.size() << ")." << std::endl;
		}
};
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
int main() {  // The client code changes only because we opt to place the new types of WatchableItems on the shelf.
	WatchableCategory *newReleaseVHS = new WatchableCategory ("New Releases VHS", 5.75), *newReleaseDVD = new WatchableCategory ("New Releases DVD", 7.25),
		*generalVHS = new WatchableCategory  ("General VHS", 3.25), *generalDVD = new WatchableCategory ("General DVD", 5.25),
		*documentaryDVD = new WatchableCategory ("Documentary DVD", 3.49), *howToVHS = new WatchableCategory ("How-To-Do-It VHS", 3.25),  // There are no DVDs for the How-To-Do-It category due to lack of interest from the consumers.
		*howToSeriesDVDOneWeekRental = new WatchableCategory ("How-To-Do-It in Series (One week rental)", 12.25);  // If there is to be a shorter or longer duration for rental, create a new WatchableCategory for those (with their adjusted rental prices).
	RatedMovie *starWarsMovieVHS = new RatedMovie (newReleaseVHS, "Star Wars", SciFi, PG), *starWarsMovieDVD = new RatedMovie (newReleaseDVD, "Star Wars", SciFi, PG);
	Documentary* NASA = new Documentary (documentaryDVD, "NASA");
	HowTo* howToCook = new HowTo (howToVHS, "How To Cook");
	const std::string HowToPlaySnooker = "How To Play Snooker (3-part series)";
	PartOfSeries *playSnookerPart1 = new PartOfSeries (howToSeriesDVDOneWeekRental, HowToPlaySnooker, 1, "Potting the Ball"),
		*playSnookerPart2 = new PartOfSeries (howToSeriesDVDOneWeekRental, HowToPlaySnooker, 2, "Cue Ball Control"),
		*playSnookerPart3 = new PartOfSeries (howToSeriesDVDOneWeekRental, HowToPlaySnooker, 3, "Tournament Preparation");
	PartOfSeries* snookerParts[] = {playSnookerPart1, playSnookerPart2, playSnookerPart3};
	ShowSeries* howToPlaySnookerParts1To3 = new ShowSeries (howToSeriesDVDOneWeekRental, HowToPlaySnooker);
	for (PartOfSeries* x : snookerParts)
		howToPlaySnookerParts1To3->addPart(x);
	
	std::list<RentableItem*> inventory;
	for (int i = 0; i < 6; i++)
		inventory.emplace_back (new VideoTape (starWarsMovieVHS));
	for (int i = 0; i < 4; i++)
		inventory.emplace_back (new DVD (starWarsMovieDVD));
	for (int i = 0; i < 4; i++)
		inventory.emplace_back (new DVD (NASA));
	for (int i = 0; i < 2; i++)
		inventory.emplace_back (new VideoTape (howToCook));
	inventory.emplace_back (new DVD (howToPlaySnookerParts1To3));  // Only one copy of this item will be placed because not too many people want to watch it.
	for (RentableItem* x: inventory)
		x->print();
		
	std::cout << std::endl << "Star Wars is no longer a new release and is moved to category General." << std::endl << std::endl;
	starWarsMovieVHS->changeCategory (generalVHS);  // changeCategory is now a method in WatchableItem, so we could call changeCategory for NASA, howToCook, and howToPlaySnookerParts1To3 too if we wanted to.
	starWarsMovieDVD->changeCategory (generalDVD); 
	for (RentableItem* x: inventory)
		x->print();
	
	Person stephen ("Stephen Hendry", 20.00);
	RentableItem* stephensDVD = *std::find_if (inventory.begin(), inventory.end(), [howToPlaySnookerParts1To3](const RentableItem* x)->bool {return x->getShow() == howToPlaySnookerParts1To3;});
	stephen.rentItem (stephensDVD, inventory);  // 'howToPlaySnookerParts1To3' being rented from 'inventory'
	stephen.play (stephensDVD);  // Using the PartOfSeries::play() function of the Composite Pattern.
	stephen.returnItem (stephensDVD, inventory);
	std::cin.get();
}

/*
Output:

Video tape with title "Star Wars" and rental price $5.75 is on the shelf.
Video tape with title "Star Wars" and rental price $5.75 is on the shelf.
Video tape with title "Star Wars" and rental price $5.75 is on the shelf.
Video tape with title "Star Wars" and rental price $5.75 is on the shelf.
Video tape with title "Star Wars" and rental price $5.75 is on the shelf.
Video tape with title "Star Wars" and rental price $5.75 is on the shelf.
DVD with title "Star Wars" and rental price $7.25 is on the shelf.
DVD with title "Star Wars" and rental price $7.25 is on the shelf.
DVD with title "Star Wars" and rental price $7.25 is on the shelf.
DVD with title "Star Wars" and rental price $7.25 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
Video tape with title "How To Cook" and rental price $3.25 is on the shelf.
Video tape with title "How To Cook" and rental price $3.25 is on the shelf.
DVD with title "How To Play Snooker (3-part series)" and rental price $12.25 is on the shelf.

Star Wars is no longer a new release and is moved to category General.

Video tape with title "Star Wars" and rental price $3.25 is on the shelf.
Video tape with title "Star Wars" and rental price $3.25 is on the shelf.
Video tape with title "Star Wars" and rental price $3.25 is on the shelf.
Video tape with title "Star Wars" and rental price $3.25 is on the shelf.
Video tape with title "Star Wars" and rental price $3.25 is on the shelf.
Video tape with title "Star Wars" and rental price $3.25 is on the shelf.
DVD with title "Star Wars" and rental price $5.25 is on the shelf.
DVD with title "Star Wars" and rental price $5.25 is on the shelf.
DVD with title "Star Wars" and rental price $5.25 is on the shelf.
DVD with title "Star Wars" and rental price $5.25 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
DVD with title "NASA" and rental price $3.49 is on the shelf.
Video tape with title "How To Cook" and rental price $3.25 is on the shelf.
Video tape with title "How To Cook" and rental price $3.25 is on the shelf.
DVD with title "How To Play Snooker (3-part series)" and rental price $12.25 is on the shelf.

Before renting "How To Play Snooker (3-part series)", Stephen Hendry has $20 (inventory size = 17).

After renting "How To Play Snooker (3-part series)", Stephen Hendry has $7.75 (inventory size = 16).
"Potting the Ball", part 1 of the series "How To Play Snooker (3-part series)", being played.
"Cue Ball Control", part 2 of the series "How To Play Snooker (3-part series)", being played.
"Tournament Preparation", part 3 of the series "How To Play Snooker (3-part series)", being played.
Stephen Hendry returns "How To Play Snooker (3-part series)" (inventory size = 17).
*/
Next question:

In the following code, note that SportsChannel does not care to visit MovieStars and Singers, and SingingContest only cares to visit Singers.
Furthermore, if a new concrete derived class of Celebrity were added to the hierarchy, all of the concrete CelebrityVisitor derived types would have to define their own visit override for this new type, even if any of them have no interest in visiting it. What pattern should be used to solve this problem?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>

struct CelebrityVisitor;

class Person {
	private:
		std::string name;
	public:
		Person (const std::string& n) : name(n) {}
		std::string getName() const {return name;}
};

struct Celebrity : public Person {
	using Person::Person;
	virtual void accept (const CelebrityVisitor&) const = 0;
};

struct MovieStar : Celebrity {
	using Celebrity::Celebrity;
	virtual void accept (const CelebrityVisitor&) const override;
};

struct Singer : Celebrity {
	using Celebrity::Celebrity;
	virtual void accept (const CelebrityVisitor&) const override;
};

struct TennisPlayer : Celebrity {
	using Celebrity::Celebrity;
	virtual void accept (const CelebrityVisitor&) const override;
};

struct Sprinter : Celebrity {
	using Celebrity::Celebrity;
	virtual void accept (const CelebrityVisitor&) const override;
};

struct CelebrityVisitor {
	virtual void visit (const MovieStar&) const = 0;
	virtual void visit (const Singer&) const = 0;
	virtual void visit (const TennisPlayer&) const = 0;
	virtual void visit (const Sprinter&) const = 0;
};

struct TVStation : CelebrityVisitor {
	virtual void visit (const MovieStar& p) const override {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Singer& p) const override {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const TennisPlayer& p) const override {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Sprinter& p) const override {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
};

struct SportsChannel : CelebrityVisitor {
	virtual void visit (const MovieStar&) const override {}
	virtual void visit (const Singer&) const override {}
	virtual void visit (const TennisPlayer& p) const override {std::cout << "Sports channel interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Sprinter& p) const override {std::cout << "Sports channel interview with " << p.getName() << " started." << std::endl;}
};

struct SingingContest : CelebrityVisitor {
	virtual void visit (const MovieStar&) const override {}
	virtual void visit (const Singer& p) const override {std::cout << "Singing contest interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const TennisPlayer&) const override {}
	virtual void visit (const Sprinter&) const override {}
};

void MovieStar::accept (const CelebrityVisitor& visitor) const {visitor.visit(*this);}
void Singer::accept (const CelebrityVisitor& visitor) const {visitor.visit(*this);}
void TennisPlayer::accept (const CelebrityVisitor& visitor) const {visitor.visit(*this);}
void Sprinter::accept (const CelebrityVisitor& visitor) const {visitor.visit(*this);}

int main() {
	MovieStar TomCruise ("Tom Cruise");
	Singer LadyGaga ("Lady Gaga");
	TennisPlayer VenusWilliams ("Venus Williams");
	Sprinter UsainBolt ("Usain Bolt");
	TVStation NBC;
	SportsChannel TSN;
	SingingContest AmericanIdol;
	Celebrity* celebrities[] = {&TomCruise, &LadyGaga, &VenusWilliams, &UsainBolt};
	CelebrityVisitor* interviewers[] = {&NBC, &TSN, &AmericanIdol};
	
	for (const CelebrityVisitor* visitor : interviewers)
		for (const Celebrity* c : celebrities)
			c->accept(*visitor);
	std::cin.get();
}
*/
Output:
TV station interview with Tom Cruise started.
TV station interview with Lady Gaga started.
TV station interview with Venus Williams started.
TV station interview with Usain Bolt started.
Sports channel interview with Venus Williams started.
Sports channel interview with Usain Bolt started.
Singing contest interview with Lady Gaga started.
*/
Last edited on
Use Acyclic Visitor Pattern. Note that if a new class is added to the Celebrity hierarchy, no new visit overrides need to be defined for TVStation, SportsChannel, or SingingContest (unless they wish to visit the new class):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>

struct CelebrityVisitor;

class Person {
	private:
		std::string name;
	public:
		Person (const std::string& n) : name(n) {}
		std::string getName() const {return name;}
};

struct Celebrity : public Person {
	using Person::Person;
	virtual void accept (const CelebrityVisitor&) const = 0;
};

struct MovieStar : Celebrity {
	using Celebrity::Celebrity;
	struct Visitor {
		virtual void visit (const MovieStar&) const = 0;
	};
	virtual void accept (const CelebrityVisitor&) const override;
};

struct Singer : Celebrity {
	using Celebrity::Celebrity;
	struct Visitor {
		virtual void visit (const Singer&) const = 0;
	};
	virtual void accept (const CelebrityVisitor&) const override;
};

struct TennisPlayer : Celebrity {
	using Celebrity::Celebrity;
	struct Visitor {
		virtual void visit (const TennisPlayer&) const = 0;
	};
	virtual void accept (const CelebrityVisitor&) const override;
};

struct Sprinter : Celebrity {
	using Celebrity::Celebrity;
	struct Visitor {
		virtual void visit (const Sprinter&) const = 0;
	};
	virtual void accept (const CelebrityVisitor&) const override;
};

struct CelebrityVisitor {  // Degenerate class now, but must have at least one virtual function to allow dynamic_cast.
	virtual ~CelebrityVisitor() = default;  // Should be made pure to prevent instantiation, but GCC won't accept it.
};

struct TVStation : CelebrityVisitor, MovieStar::Visitor, Singer::Visitor, TennisPlayer::Visitor, Sprinter::Visitor {
	virtual void visit (const MovieStar& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Singer& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const TennisPlayer& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Sprinter& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
};

struct SportsChannel : CelebrityVisitor, TennisPlayer::Visitor, Sprinter::Visitor {  // Do not derive from MovieStar::Visitor or Singer::Visitor.
	virtual void visit (const TennisPlayer& p) const {std::cout << "Sports channel interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Sprinter& p) const {std::cout << "Sports channel interview with " << p.getName() << " started." << std::endl;}
};

struct SingingContest : CelebrityVisitor, Singer::Visitor {  // Do not derive from MovieStar::Visitor, TennisPlayer::Visitor, or Sprinter::Visitor.
	virtual void visit (const Singer& p) const {std::cout << "Singing contest interview with " << p.getName() << " started." << std::endl;}
};

void MovieStar::accept (const CelebrityVisitor& visitor) const {
	const Visitor* v = dynamic_cast<const Visitor*>(&visitor);
	if (v) v->visit(*this);
}

void Singer::accept (const CelebrityVisitor& visitor) const {
	const Visitor* v = dynamic_cast<const Visitor*>(&visitor);
	if (v) v->visit(*this);
}

void TennisPlayer::accept (const CelebrityVisitor& visitor) const {
	const Visitor* v = dynamic_cast<const Visitor*>(&visitor);
	if (v) v->visit(*this);
}

void Sprinter::accept (const CelebrityVisitor& visitor) const {
	const Visitor* v = dynamic_cast<const Visitor*>(&visitor);
	if (v) v->visit(*this);
}

int main() {
	MovieStar TomCruise ("Tom Cruise");
	Singer LadyGaga ("Lady Gaga");
	TennisPlayer VenusWilliams ("Venus Williams");
	Sprinter UsainBolt ("Usain Bolt");
	TVStation NBC;
	SportsChannel TSN;
	SingingContest AmericanIdol;
	Celebrity* celebrities[] = {&TomCruise, &LadyGaga, &VenusWilliams, &UsainBolt};
	CelebrityVisitor* interviewers[] = {&NBC, &TSN, &AmericanIdol};
	
	for (const CelebrityVisitor* visitor : interviewers)
		for (const Celebrity* c : celebrities)
			c->accept(*visitor);
	std::cin.get();
}

/*
Output:
TV station interview with Tom Cruise started.
TV station interview with Lady Gaga started.
TV station interview with Venus Williams started.
TV station interview with Usain Bolt started.
Sports channel interview with Venus Williams started.
Sports channel interview with Usain Bolt started.
Singing contest interview with Lady Gaga started.
*/



Exercise: Refine the above so that the repeat definitions for the four accept functions need only be given once. Which design pattern should be used for that?
To avoid having to repeat the definitions for the four accept overrides, use Curiously Recurring Template Pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <iostream>

struct CelebrityVisitor;

class Person {
	private:
		std::string name;
	public:
		Person (const std::string& n) : name(n) {}
		std::string getName() const {return name;}
};

struct Celebrity : public Person {
	using Person::Person;
	virtual void accept (const CelebrityVisitor&) const = 0;
};

template <typename DERIVED>
class CelebrityCRTP : public Celebrity {  // Add this class between Celebrity and its concrete derived types.
	protected:
		using Celebrity::Celebrity;
		virtual void accept (const CelebrityVisitor&) const override;
};

struct MovieStar : CelebrityCRTP<MovieStar> {
	using CelebrityCRTP<MovieStar>::CelebrityCRTP;  // inherits CelebrityCRTP<MovieStar>'s constructor instead now
	struct Visitor {
		virtual void visit (const MovieStar&) const = 0;
	};
//	virtual void accept (const CelebrityVisitor&) const override;  // removed, as it inherits from CelebrityCRTP<MovieStar> now
};

struct Singer : CelebrityCRTP<Singer> {
	using CelebrityCRTP<Singer>::CelebrityCRTP;
	struct Visitor {
		virtual void visit (const Singer&) const = 0;
	};
//	virtual void accept (const CelebrityVisitor&) const override;  // removed
};

struct TennisPlayer : CelebrityCRTP<TennisPlayer> {
	using CelebrityCRTP<TennisPlayer>::CelebrityCRTP;
	struct Visitor {
		virtual void visit (const TennisPlayer&) const = 0;
	};
//	virtual void accept (const CelebrityVisitor&) const override;  // removed
};

struct Sprinter : CelebrityCRTP<Sprinter> {
	using CelebrityCRTP<Sprinter>::CelebrityCRTP;
	struct Visitor {
		virtual void visit (const Sprinter&) const = 0;
	};
//	virtual void accept (const CelebrityVisitor&) const override;  // removed
};

struct CelebrityVisitor {  // Degenerate class now, but must have at least one virtual function to allow dynamic_cast.
	virtual ~CelebrityVisitor() = default;  // Should be made pure to prevent instantiation, but GCC won't accept it.
};

struct TVStation : CelebrityVisitor, MovieStar::Visitor, Singer::Visitor, TennisPlayer::Visitor, Sprinter::Visitor {
	virtual void visit (const MovieStar& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Singer& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const TennisPlayer& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Sprinter& p) const {std::cout << "TV station interview with " << p.getName() << " started." << std::endl;}
};

struct SportsChannel : CelebrityVisitor, TennisPlayer::Visitor, Sprinter::Visitor {  // Do not derive from MovieStar::Visitor or Singer::Visitor.
	virtual void visit (const TennisPlayer& p) const {std::cout << "Sports channel interview with " << p.getName() << " started." << std::endl;}
	virtual void visit (const Sprinter& p) const {std::cout << "Sports channel interview with " << p.getName() << " started." << std::endl;}
};

struct SingingContest : CelebrityVisitor, Singer::Visitor {  // Do not derive from MovieStar::Visitor, TennisPlayer::Visitor, or Sprinter::Visitor.
	virtual void visit (const Singer& p) const {std::cout << "Singing contest interview with " << p.getName() << " started." << std::endl;}
};

template <typename DERIVED>
void CelebrityCRTP<DERIVED>::accept (const CelebrityVisitor& visitor) const {  // Definition is needed only once now.
	const typename DERIVED::Visitor* v = dynamic_cast<const typename DERIVED::Visitor*>(&visitor);
	if (v) v->visit (dynamic_cast<const DERIVED&>(*this));
}

int main() {
	MovieStar TomCruise ("Tom Cruise");
	Singer LadyGaga ("Lady Gaga");
	TennisPlayer VenusWilliams ("Venus Williams");
	Sprinter UsainBolt ("Usain Bolt");
	TVStation NBC;
	SportsChannel TSN;
	SingingContest AmericanIdol;
	Celebrity* celebrities[] = {&TomCruise, &LadyGaga, &VenusWilliams, &UsainBolt};
	CelebrityVisitor* interviewers[] = {&NBC, &TSN, &AmericanIdol};
	
	for (const CelebrityVisitor* visitor : interviewers)
		for (const Celebrity* c : celebrities)
			c->accept(*visitor);
	std::cin.get();
}

/*
Output:
TV station interview with Tom Cruise started.
TV station interview with Lady Gaga started.
TV station interview with Venus Williams started.
TV station interview with Usain Bolt started.
Sports channel interview with Venus Williams started.
Sports channel interview with Usain Bolt started.
Singing contest interview with Lady Gaga started.
*/
Topic archived. No new replies allowed.