O
Oplec
Hi,
I am learning standard C++ as a hobby. The C++ Programming Language :
Special Edition has been the principal source for my information. I read the
entirety of the book and concluded that I had done myself a disservice by
having not attempted and completed the exercises. I intend to rectify that.
My current routine is to read a chapter and then attempt every exercise
problem, and I find that this process is leading to a greater understanding
of the language and implementation.
However, occasionly I am unable to complete some of the exercises, or am not
satisfied with my method of solution. One such exercise is 12.7[2].
From what I read in the chapter and a few papers of his, Mr. Stroustrup
suggests that most of the time the best implemention for the Shape, Dot,
Line, Rectangle hierarchy would be for Shape to be a purely abstract base
class, and for Dot, Line, Rectangle to inheirit from Shape and implement the
virtual functions. In his code examples he routinely defines "virtual void
draw() = 0" as a member of the Shape class. Upon implementing a solution to
12.7[2] I experienced a difficulty surrounding how to implement the drawing
of Shapes for the Window class that must exist. My implementation was for
simple console mode character output using std::cout.
Since I am new to OO programming, I feel that I lack some understanding of
what he intended my solution to be similar to. I have read that it is good
to keep separate ideas, separate. And, using this idea, I run into
difficulty on how to have Dots, Lines, Rectangles, draw themselves, since
they need access to a method of rendering to the screen, but the rendering
must be controlled because I am using console mode character output. Also,
from reading 12.7[5], it seems that the rendering of the shapes needs to
controlled by an intermediary object so that the clipping can be performed.
I am at a loss as to how to complete this exercise in a properly
object-oriented fashion. I wish to understand how to implement C++ in the
best possible way, and that is why I am not satisfied with my current
understanding. If you have the time, and the experience, please share you
knowledge in this matter.
The following is my basic design used to complete 12.7[2], but what I feel
is a very poor design and should not be accepted as a solution. I offer it
in hopes that experienced programmers may better understand my problem, and,
perhaps, offer a better solution. Only the classes Point, Shape, Dot, and
Window will be shown, as their interaction is the same as that for Line, and
Rectangle.
(This is my first post, and I'm not sure how to include a compressed file of
my solution, so please accept this.)
Shape was kept purely abstract, but I added "virtual void draw(Window*
const) const = 0;". I added this because upon implementation I ran into the
problem of how the shapes would be able to access a method of rending to the
screen. What happens is that a Window object, w, has its "draw(Shape*)"
member called. Window::draw(Shape*) then passes its "this" pointer to
"Shape::draw(Window*)". The Shape::draw member then uses the Window pointer
to access Window:ut_pixel(Point, char). Window:ut_pixel(Point, char)
then adds a pair<Point, char> to a vector object, which is used when
Window::show() is called. The vector is used as a sort of buffer for the
shapes to be drawn to so that multiple shapes can be drawn correctly since I
am using a console mode and cout to render characters.
To draw something, the following would be used:
Window w(10,10);
w.draw(Dot(Point(5,5));
w.show();
//***************************
// Point.h
// This is a structure because Mr. Stroustrup suggests that classes
// with hidden members, and access members should only be
// used when there is an invariant to enforce, and Point
// does not have an invariant.
// **************************
#ifndef POINT_H
#define POINT_H
struct Point {
Point() :x(0), y(0) { }
Point(int i, int j) :x(i), y(j) { }
int x, y;
};
#endif
// ***************************
// Shape.h
// Mr. Stroustrup usually declares
// virtual void draw() = 0;
// My design has to pass a Window object.
// ***************************
#ifndef SHAPE_H
#define SHAPE_H
#include "point.h"
class Window;
struct Shape {
virtual Point n() const = 0;
virtual Point e() const = 0;
virtual Point s() const = 0;
virtual Point w() const = 0;
virtual Point ne() const = 0;
virtual Point nw() const = 0;
virtual Point se() const = 0;
virtual Point sw() const = 0;
virtual ~Shape() {}
virtual void draw(Window* const) const = 0;
};
#endif
// ******************************
// Dot.h
// ******************************
#ifndef DOT_H
#define DOT_H
#include "point.h"
#include "shape.h"
class Window;
class Dot : public Shape {
public:
Dot();
Dot(const Point&);
Point n() const;
Point e() const;
Point s() const;
Point w() const;
Point ne() const;
Point nw() const;
Point se() const;
Point sw() const;
void draw(Window* const) const;
private:
Point p_;
};
// Inline definitions
inline Point Dot::n() const { return p_; }
inline Point Dot::e() const { return p_; }
inline Point Dot::s() const { return p_; }
inline Point Dot::w() const { return p_; }
inline Point Dot::ne() const { return p_; }
inline Point Dot::nw() const { return p_; }
inline Point Dot::se() const { return p_; }
inline Point Dot::sw() const { return p_; }
#endif
// ********************
// Dot.cpp
// ********************
#include "dot.h"
#include "window.h"
Dot:ot() {}
Dot:ot(const Point& p)
: p_(p)
{
}
void Dot::draw(Window* const w) const
{
w->put_pixel(p_, '*');
}
// ****************************
// Window.h
// ****************************
#ifndef WINDOW_H
#define WINDOW_H
#include "point.h"
#include "utility"
#include "vector"
class Shape;
class Window {
public:
Window(const int, const int);
void draw(const Shape&);
Point current() const;
void current(const Point&);
void put_pixel(const Point&, const char&);
void show() const;
void clear();
private:
int w_, h_;
Point c_;
std::vector<std:air<Point, char> > points;
};
#endif
// *************************
// Window.cpp
// *************************
#include "window.h"
#include "shape.h"
#include <iostream>
Window::Window(const int x, const int y)
: w_(x), h_(y) { }
void Window::draw(const Shape& s)
{
s.draw(this);
c_ = s.se();
}
Point Window::current() const
{
return c_;
}
void Window::current(const Point& c)
{
c_ = c;
}
void Window:ut_pixel(const Point& p, const char& c)
{
points.push_back(std::make_pair(p, c));
}
// This searches the vector for each character position of the window.
// If a character is found, it is written to the screen.
// If a character is not found, a space is written to the screen.
void Window::show() const
{
for (int j=0; j <= h_; j++) {
for (int i=0; i <= w_; i++) {
typedef std::vector<std:air<Point,char> >::const_iterator CI;
CI k;
for (k=points.begin(); k != points.end(); ++k) {
if (k->first.x == i && k->first.y == j) {
std::cout << k->second;
break;
}
}
if (k == points.end()) std::cout << ' ';
}
std::cout << '\n';
}
}
void Window::clear()
{
points.clear();
}
I am learning standard C++ as a hobby. The C++ Programming Language :
Special Edition has been the principal source for my information. I read the
entirety of the book and concluded that I had done myself a disservice by
having not attempted and completed the exercises. I intend to rectify that.
My current routine is to read a chapter and then attempt every exercise
problem, and I find that this process is leading to a greater understanding
of the language and implementation.
However, occasionly I am unable to complete some of the exercises, or am not
satisfied with my method of solution. One such exercise is 12.7[2].
From what I read in the chapter and a few papers of his, Mr. Stroustrup
suggests that most of the time the best implemention for the Shape, Dot,
Line, Rectangle hierarchy would be for Shape to be a purely abstract base
class, and for Dot, Line, Rectangle to inheirit from Shape and implement the
virtual functions. In his code examples he routinely defines "virtual void
draw() = 0" as a member of the Shape class. Upon implementing a solution to
12.7[2] I experienced a difficulty surrounding how to implement the drawing
of Shapes for the Window class that must exist. My implementation was for
simple console mode character output using std::cout.
Since I am new to OO programming, I feel that I lack some understanding of
what he intended my solution to be similar to. I have read that it is good
to keep separate ideas, separate. And, using this idea, I run into
difficulty on how to have Dots, Lines, Rectangles, draw themselves, since
they need access to a method of rendering to the screen, but the rendering
must be controlled because I am using console mode character output. Also,
from reading 12.7[5], it seems that the rendering of the shapes needs to
controlled by an intermediary object so that the clipping can be performed.
I am at a loss as to how to complete this exercise in a properly
object-oriented fashion. I wish to understand how to implement C++ in the
best possible way, and that is why I am not satisfied with my current
understanding. If you have the time, and the experience, please share you
knowledge in this matter.
The following is my basic design used to complete 12.7[2], but what I feel
is a very poor design and should not be accepted as a solution. I offer it
in hopes that experienced programmers may better understand my problem, and,
perhaps, offer a better solution. Only the classes Point, Shape, Dot, and
Window will be shown, as their interaction is the same as that for Line, and
Rectangle.
(This is my first post, and I'm not sure how to include a compressed file of
my solution, so please accept this.)
Shape was kept purely abstract, but I added "virtual void draw(Window*
const) const = 0;". I added this because upon implementation I ran into the
problem of how the shapes would be able to access a method of rending to the
screen. What happens is that a Window object, w, has its "draw(Shape*)"
member called. Window::draw(Shape*) then passes its "this" pointer to
"Shape::draw(Window*)". The Shape::draw member then uses the Window pointer
to access Window:ut_pixel(Point, char). Window:ut_pixel(Point, char)
then adds a pair<Point, char> to a vector object, which is used when
Window::show() is called. The vector is used as a sort of buffer for the
shapes to be drawn to so that multiple shapes can be drawn correctly since I
am using a console mode and cout to render characters.
To draw something, the following would be used:
Window w(10,10);
w.draw(Dot(Point(5,5));
w.show();
//***************************
// Point.h
// This is a structure because Mr. Stroustrup suggests that classes
// with hidden members, and access members should only be
// used when there is an invariant to enforce, and Point
// does not have an invariant.
// **************************
#ifndef POINT_H
#define POINT_H
struct Point {
Point() :x(0), y(0) { }
Point(int i, int j) :x(i), y(j) { }
int x, y;
};
#endif
// ***************************
// Shape.h
// Mr. Stroustrup usually declares
// virtual void draw() = 0;
// My design has to pass a Window object.
// ***************************
#ifndef SHAPE_H
#define SHAPE_H
#include "point.h"
class Window;
struct Shape {
virtual Point n() const = 0;
virtual Point e() const = 0;
virtual Point s() const = 0;
virtual Point w() const = 0;
virtual Point ne() const = 0;
virtual Point nw() const = 0;
virtual Point se() const = 0;
virtual Point sw() const = 0;
virtual ~Shape() {}
virtual void draw(Window* const) const = 0;
};
#endif
// ******************************
// Dot.h
// ******************************
#ifndef DOT_H
#define DOT_H
#include "point.h"
#include "shape.h"
class Window;
class Dot : public Shape {
public:
Dot();
Dot(const Point&);
Point n() const;
Point e() const;
Point s() const;
Point w() const;
Point ne() const;
Point nw() const;
Point se() const;
Point sw() const;
void draw(Window* const) const;
private:
Point p_;
};
// Inline definitions
inline Point Dot::n() const { return p_; }
inline Point Dot::e() const { return p_; }
inline Point Dot::s() const { return p_; }
inline Point Dot::w() const { return p_; }
inline Point Dot::ne() const { return p_; }
inline Point Dot::nw() const { return p_; }
inline Point Dot::se() const { return p_; }
inline Point Dot::sw() const { return p_; }
#endif
// ********************
// Dot.cpp
// ********************
#include "dot.h"
#include "window.h"
Dot:ot() {}
Dot:ot(const Point& p)
: p_(p)
{
}
void Dot::draw(Window* const w) const
{
w->put_pixel(p_, '*');
}
// ****************************
// Window.h
// ****************************
#ifndef WINDOW_H
#define WINDOW_H
#include "point.h"
#include "utility"
#include "vector"
class Shape;
class Window {
public:
Window(const int, const int);
void draw(const Shape&);
Point current() const;
void current(const Point&);
void put_pixel(const Point&, const char&);
void show() const;
void clear();
private:
int w_, h_;
Point c_;
std::vector<std:air<Point, char> > points;
};
#endif
// *************************
// Window.cpp
// *************************
#include "window.h"
#include "shape.h"
#include <iostream>
Window::Window(const int x, const int y)
: w_(x), h_(y) { }
void Window::draw(const Shape& s)
{
s.draw(this);
c_ = s.se();
}
Point Window::current() const
{
return c_;
}
void Window::current(const Point& c)
{
c_ = c;
}
void Window:ut_pixel(const Point& p, const char& c)
{
points.push_back(std::make_pair(p, c));
}
// This searches the vector for each character position of the window.
// If a character is found, it is written to the screen.
// If a character is not found, a space is written to the screen.
void Window::show() const
{
for (int j=0; j <= h_; j++) {
for (int i=0; i <= w_; i++) {
typedef std::vector<std:air<Point,char> >::const_iterator CI;
CI k;
for (k=points.begin(); k != points.end(); ++k) {
if (k->first.x == i && k->first.y == j) {
std::cout << k->second;
break;
}
}
if (k == points.end()) std::cout << ' ';
}
std::cout << '\n';
}
}
void Window::clear()
{
points.clear();
}