I am working an exercise from Chapter 12 of Bjarne Stroustrup's Programming Principles and Practice Using C++.
The graphics interface library in this chapter, Simple_window.h
and Graph.h
provides a simple wrapper around FLTK. Source code is available at http://www.stroustrup.com/Programming/Graphics/
To attach a Shape
to a Window
, users must provide a reference to a Shape
object, which must exist for the lifetime of the Window
. The relevant member function has the following signature:
void Graph_lib::Window::attach(Shape& s);
Referencing the comments in Difference in make_shared and normal shared_ptr in C++ I would like to make use of std::shared_ptr
and std::make_shared
to ensure that the allocated Rectangle
objects (which derive from Shape
) match the lifetime of the Simple_window
object (which derives from Window
).
First solution:
#include <iostream>
#include <vector>
#include <memory>
#include <stdexcept>
#include "Simple_window.h"
#include "Graph.h"
/*
Exercise 4 from Chapter 12
Programming Principles and Practice using C++, page 434
Draw a checkers board: 8-by-8 alternating white and red squares.
*/
int main()
{
using namespace std;
using namespace Graph_lib;
using Graph_lib::Rectangle;
try {
Simple_window win(Point(20,20),400,300,"Exercise 12.4");
vector<shared_ptr<Shape>> shapes;
const auto width = 20;
const auto height = 20;
for (auto row = 0; row < 8; row++) {
for (auto col = 0; col < 8; col++) {
auto x = col * width;
auto y = row * height;
shared_ptr<Rectangle> r(new Rectangle(Point(x,y),width,height));
shapes.push_back(r);
if (row % 2 == 0) {
r->set_fill_color((col % 2 == 0) ? Color::white : Color::red);
} else {
r->set_fill_color((col % 2 == 1) ? Color::white : Color::red);
}
win.attach(*r);
}
}
win.wait_for_button();
}
catch(exception &e) {
cerr << "Exception occured: " << e.what() << endl;
return 1;
}
catch(...) {
cerr << "Unknown exception occured" << endl;
return 2;
}
}
To make the use of the smart pointer less explicit I extended Simple_window
with a user-defined member function which pushes the smart pointers into the vector before passing the references along to Simple_window::attach(Shape&)
:
struct Shared_simple_window : Simple_window {
Shared_simple_window(Point xy, int w, int h, const std::string& title )
: Simple_window(xy, w, h, title) { }
void attach(std::shared_ptr<Graph_lib::Shape>&& s)
{ shared_shapes.push_back(s); Simple_window::attach(*s.get()); }
private:
std::vector<std::shared_ptr<Graph_lib::Shape>> shared_shapes;
};
The code in the try block now becomes
Shared_simple_window win(Point(20,20),400,300,"Exercise 12.4");
const auto width = 20;
const auto height = 20;
for (auto row = 0; row < 8; row++) {
for (auto col = 0; col < 8; col++) {
auto x = col * width;
auto y = row * height;
shared_ptr<Rectangle> r(new Rectangle(Point(x,y),width,height));
if (row % 2 == 0) {
r->set_fill_color((col % 2 == 0) ? Color::white : Color::red);
} else {
r->set_fill_color((col % 2 == 1) ? Color::white : Color::red);
}
win.attach(r);
}
}
win.wait_for_button();
This works, but my question is, how can I make use of make_shared
in this example rather than new
? I tried replacing the shared_ptr
declaration with the following
auto r = make_shared<Rectangle>(Rectangle(Point(x,y),width,height));
but I get an error use of deleted function Graph_lib::Rectangle::Rectangle(Graph_lib::Rectangle&&)
, probably because Rectangle
is not designed to be moved. Should I extend Rectangle
in order for it to work?