By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
445,771 Members | 1,709 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 445,771 IT Pros & Developers. It's quick & easy.

How would Mr. Stroustrup implement his solution to 12.7[2]

P: n/a
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::put_pixel(Point, char). Window::put_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::Dot() {}

Dot::Dot(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::pair<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::put_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::pair<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();
}
Jul 19 '05 #1
Share this Question
Share on Google+
26 Replies


P: n/a
Oplec <an*******@anonymous.com> wrote in message
news:FM*******************@twister01.bloor.is.net. cable.rogers.com...
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.
That's reasonable. In a real graphics system you want to be able to render a
drawing on different devices - any window, a printer, a plotter etc., but
you don't want the shape object itself to be tied down to a single device.
Passing in the device to draw on is therefore a good idea.
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*)".
I don't think this is such a good idea. Since the Shape object is doing the
drawing, there's no need for the Window class to know about Shapes. Just
pass a reference to the Window object to the Shape's draw(), and the
overridden function in the specific shape will draw itself on the Window.
The Shape::draw member then uses the Window pointer
to access Window::put_pixel(Point, char). Window::put_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.
That all sounds good to me.
To draw something, the following would be used:

Window w(10,10);
w.draw(Dot(Point(5,5));
w.show();
I would have:

Window w(10,10);
Dot dot(Point(5,5));
dot.draw(w);

[most code snipped]

One comment on the code: void draw(Window* const) const;
Window(const int, const int);


You can make your function parameters const if you want, but it's not usual
or necessary. Parameters are passed by value, so functions have their own
copies of them. It doesn't help the caller of a function know that a
function isn't going to modify its own copy of what's passed to it. The
caller doesn't care.

If you haven't done OO before and you are inexperienced in C++, I think
you're doing pretty well.

DW

Jul 19 '05 #2

P: n/a
----- Original Message -----
From: "David White" <no@email.provided>
Newsgroups: comp.lang.c++
Sent: Tuesday, October 21, 2003 8:40 PM
Subject: Re: How would Mr. Stroustrup implement his solution to 12.7[2]

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*)".
I don't think this is such a good idea. Since the Shape object is doing

the drawing, there's no need for the Window class to know about Shapes. Just
pass a reference to the Window object to the Shape's draw(), and the
overridden function in the specific shape will draw itself on the Window.
The Window class has to have a draw() member because such a member is
included in the example use of the final implementation of problem 12.7[2].
He shows that a Window, w, should be used as in the following:
w.draw(Circle(w.current(),10)); So, clearly, he intends for the solutions'
Window class to have a draw() member that takes a pointer to a Shape class.
I completely understand your idea, and would do it that way as well, but I
wish to complete this the way he intended it to be. One of my main
objections to my design was the addition of Shape::draw(Window*) because in
all the examples I have seen surrounding the Shape example, Shape only ever
has a pure virtual Shape::draw(), and that is why I felt my solution should
not be accepted. I often wondered if he mean't for the Window class to use
iterators supplied by each Shape object in order to get the Shape's shape;
that is, the character types, and position to be rendered on screen. As of
this writing, I have yet to read anything of his that had Shapes provide
iterators.

He uses examples like the following when illustrating proper design for this
example:
(src: http://technetcast.ddj.com/str0237/bs-millennium.pdf)

class Shape {
public:
virtual void draw() = 0;
virtual void rotate(double) = 0;
};

class Common { Color c; /* ... */ };

class Circle : public Shape, protected Common { /* ... */ };
As you can see, his Shape's draw() member does not take anything. This is
the difficult aspect causing me the concern with my design, as I feel that
there must be an elegant design that he had intended to be used. My adding
that Shape::draw() take a Window object was my method of solving 12.7[2],
while adhering to the requirement their be Window::draw(Shape&);

(Note, I mean't Shape& previously when writing Shape*).

I truly wish to understand proper OO design, and do not wish to continue my
study until I have understood what should be a very simple problem.

You can make your function parameters const if you want, but it's not usual or necessary. Parameters are passed by value, so functions have their own
copies of them. It doesn't help the caller of a function know that a
function isn't going to modify its own copy of what's passed to it. The
caller doesn't care.
Your comment is interesting. In TC++PL, Mr. Stroustrup uses non-const ints
for parameters over const ints, but I thought he was doing that for some
reason that I had not learned about yet and so was using "const int"; that
is, using const on parameters passed by value. I did this because I wanted
to make sure in my function definitions that I would not modify the original
values that were passed. I understand that changing the values would not
affect the callers, but I did it so that my use of the variables would not
alter them. If that is not a good idea, please indicate why. I wish to do
such things correctly.
If you haven't done OO before and you are inexperienced in C++, I think
you're doing pretty well.

DW


Thank you very much for your feedback, as I found it most helpful! I am
glad a more experienced person thinks I am doing alright because I have not
talked to anyone about this and so had only my own opinion of myself. Not
having anyone to communicate with in regards to C++ can be difficult at
times.
Jul 19 '05 #3

P: n/a
On Wed, 22 Oct 2003 01:25:48 GMT, "Oplec" <an*******@anonymous.com> wrote:
He uses examples like the following when illustrating proper design for this
example:
(src: http://technetcast.ddj.com/str0237/bs-millennium.pdf)

class Shape {
public:
virtual void draw() = 0;
virtual void rotate(double) = 0;
};

class Common { Color c; /* ... */ };

class Circle : public Shape, protected Common { /* ... */ };
As you can see, his Shape's draw() member does not take anything. This is
the difficult aspect causing me the concern with my design, as I feel that
there must be an elegant design that he had intended to be used.
Not very elegant, but there are various solutions _given_ the interface
above -- but I don't think Bjarne meant for that to be followed to the
letter, just in spirit.

The idea of passing a drawing surface object to the 'draw' function is
probably the most elegant and natural solution. I'd pass that by reference,
not as a pointer, because a pointer can be NULL. And I'd pass that as an
abstract interface, not as a concrete 'Window', but that's just details.

If you want to avoid an argument to 'draw' you can simply hook up your
object to the drawing surface (or window, if you want). That's not very
elegant, but works. E.g., pass the drawing surface object in to the 'Circle'
or 'Line' or whatever constructor, let each object keep a reference. Or
allow for more dynamic attachment and detachment. Although that's ugly and
fraught with dangers.
My adding
that Shape::draw() take a Window object was my method of solving 12.7[2],
while adhering to the requirement their be Window::draw(Shape&);


It would help if you posted the text of that exercise. Not all of us have
a recent edition of TCPPPL. Mine is from 1987, I think (I did have a more
recent edition, but someone borrowed it without my knowledge...).

That said, the best advice I can give is to trust your instincts. Yes,
Bjarne is God. And no, he's not above making a small mistake here and there
(if he were then there would be no errata list...). So don't get stuck on
following the letter of the text when you should be following the spirit of the
text. Or even -- following your own ideas and logic to wherever it leads.

Hth.

Jul 19 '05 #4

P: n/a
Oplec <an*******@anonymous.com> wrote in message
news:w0*******************@twister01.bloor.is.net. cable.rogers.com...
----- Original Message -----
From: "David White" <no@email.provided>
Newsgroups: comp.lang.c++
Sent: Tuesday, October 21, 2003 8:40 PM
Subject: Re: How would Mr. Stroustrup implement his solution to 12.7[2]

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*)".
I don't think this is such a good idea. Since the Shape object is doing

the
drawing, there's no need for the Window class to know about Shapes. Just
pass a reference to the Window object to the Shape's draw(), and the
overridden function in the specific shape will draw itself on the Window.
The Window class has to have a draw() member because such a member is
included in the example use of the final implementation of problem 12.7[2]. He shows that a Window, w, should be used as in the following:
w.draw(Circle(w.current(),10)); So, clearly, he intends for the solutions'
Window class to have a draw() member that takes a pointer to a Shape class. I completely understand your idea, and would do it that way as well, but I
wish to complete this the way he intended it to be.
Fair enough. If that's what the exercise says...
One of my main
objections to my design was the addition of Shape::draw(Window*) because in all the examples I have seen surrounding the Shape example, Shape only ever has a pure virtual Shape::draw()
Yes, I've seen these as example OO designs as well, but they are usually
designs, not working implementations. I just don't think a draw() with no
parameters is right for a real graphics system. The Shape objects should be
independent of the device because it is not only possible, but highly
likely, that you will sometimes want to print whatever you've drawn on the
screen.
, and that is why I felt my solution should
not be accepted. I often wondered if he mean't for the Window class to use
iterators supplied by each Shape object in order to get the Shape's shape;
that is, the character types, and position to be rendered on screen.
Probably. I think the shapes are defined so that the n(), s() etc. members
are sufficient for the window to draw any shape. This is probably a good
enough design, or even the best design, for a system with these limitations.
Remember it is just an exercise and not necessarily a proposal for the best
design for graphics systems in general. For high resolution graphics systems
you will want to have dotted or dashed lines, thick or thin lines, curved
lines, ellipses, pear shapes, closed shapes with various kinds of shading
etc. The possibilities and complexities are endless, and in my opinion the
shape object will usually best know how to draw itself. The type of device
might make a difference too (e.g., raster or non-raster), but the shape
object is where most of the knowledge belongs. So, for this exercise
Stroustrup's design is sufficient, but for more complex drawing I don't
think it is.
As of
this writing, I have yet to read anything of his that had Shapes provide
iterators.

He uses examples like the following when illustrating proper design for this example:
(src: http://technetcast.ddj.com/str0237/bs-millennium.pdf)
That file is not accessible to me at the moment.
class Shape {
public:
virtual void draw() = 0;
virtual void rotate(double) = 0;
};

class Common { Color c; /* ... */ };

class Circle : public Shape, protected Common { /* ... */ };
As you can see, his Shape's draw() member does not take anything. This is
the difficult aspect causing me the concern with my design, as I feel that
there must be an elegant design that he had intended to be used. My adding
that Shape::draw() take a Window object was my method of solving 12.7[2],
while adhering to the requirement their be Window::draw(Shape&);
I assure that, in general, there's nothing wrong with adding a device
parameter to the draw function. But it isn't necessary in this exercise,
since I think it's the Window that's doing the drawing.
(Note, I mean't Shape& previously when writing Shape*).

I truly wish to understand proper OO design, and do not wish to continue my study until I have understood what should be a very simple problem.

You can make your function parameters const if you want, but it's not usual
or necessary. Parameters are passed by value, so functions have their

own copies of them. It doesn't help the caller of a function know that a
function isn't going to modify its own copy of what's passed to it. The
caller doesn't care.


Your comment is interesting. In TC++PL, Mr. Stroustrup uses non-const ints
for parameters over const ints, but I thought he was doing that for some
reason that I had not learned about yet and so was using "const int"; that
is, using const on parameters passed by value.


No, I think he's using non-const parameters because there's no need to make
them const and because it's the convention.
I did this because I wanted
to make sure in my function definitions that I would not modify the original values that were passed. I understand that changing the values would not
affect the callers, but I did it so that my use of the variables would not
alter them.
Usually, programmers don't accidentally change the passed parameters. And
even if they do it won't affect anything outside the function. So, most
people don't bother making the parameters const.
If that is not a good idea, please indicate why. I wish to do
such things correctly.


My problem with it is mainly that the callers see the interface. For
example:

void f(const char * const p);

Here, the first const is important for the caller, but the second const is
only important for the function. I think it's better if function interfaces
are not cluttered with information not relevant to the caller. However,
others might disagree.

DW

Jul 19 '05 #5

P: n/a

"Oplec" <an*******@anonymous.com> wrote in message
news:w0*******************@twister01.bloor.is.net. cable.rogers.com...
You can make your function parameters const if you want, but it's not usual
or necessary. Parameters are passed by value, so functions have their own copies of them. It doesn't help the caller of a function know that a
function isn't going to modify its own copy of what's passed to it. The
caller doesn't care.


Your comment is interesting. In TC++PL, Mr. Stroustrup uses non-const ints
for parameters over const ints, but I thought he was doing that for some
reason that I had not learned about yet and so was using "const int"; that
is, using const on parameters passed by value. I did this because I wanted
to make sure in my function definitions that I would not modify the

original values that were passed. I understand that changing the values would not
affect the callers, but I did it so that my use of the variables would not
alter them. If that is not a good idea, please indicate why. I wish to do
such things correctly.


Since you explain it that way, your technique is valid. Nonetheless,
programmers very rarely code this way. Some programmers have never seen it,
and for some reason it looks so odd to them that they might think it's a
bug, or at least go off in search of the answer. Therefore, from a
maintenance perspective, it might be considered "confusing". But a
programmer's misunderstanding of a properly working language feature doesn't
mean you shouldn't use it. There's nothing wrong with it. You might want
to make a comment as to your intent, if others read your code.
Jul 19 '05 #6

P: n/a

"David White" <no@email.provided> wrote in message
news:Nl******************@nasal.pacific.net.au...

Here, the first const is important for the caller, but the second const is
only important for the function. I think it's better if function interfaces are not cluttered with information not relevant to the caller. However,
others might disagree.


In theory, I definitely agree. In practice, there are certain things that
the language requires that force your hand. For example, when declaring
your function, you don't need to put the parameter names, just the types.
This is good - technically, it exposes a tiny part of the implementation.
(Of course, you might at least want to make a comment as to what the
parameter is for if it's not obvious.

void f(int, int); // fine
void f(int length) // fine - nice feature of C++
{...}

Unfortunately, you can't do this. This would also help hide some of the
implementation (you are using the const parameter as an implementation tool
only), but the language doesn't allow it.

void f(int);
void f(const int length)
{...}

So if you want to do this, it *has* to go in the declaration and the user
will have to see it.
Jul 19 '05 #7

P: n/a

"Oplec" <an*******@anonymous.com> wrote in message
news:FM*******************@twister01.bloor.is.net. cable.rogers.com...
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.


If you still have questions, why don't you ask him personally? You would
want to distill the message down to be as succinct as possible because I'm
sure he's very busy. He has helped me when I couldn't find a complete
answer here, and in your case you're asking something directly related to
his book.
Jul 19 '05 #8

P: n/a
"Alf P. Steinbach" <al***@start.no> wrote in message
news:3f****************@News.CIS.DFN.DE...
It would help if you posted the text of that exercise. Not all of us have
a recent edition of TCPPPL. Mine is from 1987, I think (I did have a more
recent edition, but someone borrowed it without my knowledge...).


Hello,

I would copy 12.7[2] from the book and reproduce it here, but all such
rights are reserved by the publisher. However, I will do my best to
represent the idea.

Write a simple graphics system, and if you don't have access to a proper
graphics system you may use an ASCII implementation. In that implementation,
points are represented by a character position and you write a suitable
character to the screen. A Window class has a constructor Window(i,j), where
i is the width, and j the height, of an area on the screen. Points use
Cartesian coordinates. A Point is represented as a coordinate pair,
Point(x,y), but a Point is not a Shape. Window has the member
Window::current() that returns the current position. Default position for
Window is Point(0,0). w.current(p) can be used to set the current position,
where p is a Point. A line is represented by two Points, for example
Line(w.current(),p7); There is a Shape class that is the base interface for
all drawable shapes such as Lines, Dots, Rectangles, Circles. The Dot shape
is used to represent a Point that is to be drawn. All Shapes are invisible
until they are draw()n. Usage: w.draw(Line(w.current(), 20)); All the shapes
have 9 points of contact: n (north), e(east), s (south), w (west), ne
(north-east), nw (north-west), se (south-east), sw (south-west), and c
(center). After a shape is drawn, w.current() equals the shape's se().
Usage: Line(x.s(), y.c());, which will create a Line from x's south point to
y's center point. A Rectangle is created Rectangle(p1, p2), where p1 is the
rectangle's bottom left point, and p2 is its top right. Usage:
Rectangle(Point(20,20), w.current()); To demonstrate the working of your
solution, render a child's drawing of a house, that is, a roof, two windows,
the body, and a door.

The following is my understanding of the previous paragraph:
What that all means is that there should ast least be an abstract base class
named Shape that contains the virtual members n(), e(), s(), w(), ne(),
nw(), se(), sw(), c(). He did not specify a Shape::draw(), but he used such
a member in an example in the book.

The Point class simply holds Cartesian coordinates.

A Dot derived from Shape and is simply a Shape having a single Point and
represented as a single character position on the screen.

The Line is derived from Shape and is created by supplying two Points, and
drawn as a series of characters between and including those positions. I had
to work out a method to actually draw lines, and then discovered that there
is a Bressenham algorithm for doing this. It would have saved me much paper
figuring it out myself.

The Window class is created by supplying two coordinates, but I do not
believe those to be Points, but rather ints. I could be mistaken. It has a
current() member that returns the current Point, that is the last shape's
se() or Point(0,0). A current(Point) member that sets the current position.
And a draw(Shape&) that draws a shape, and is at the heart of my objection,
concern over my implementation.

That constitutes what I know for certain of the problem. My translation of
12.7[2] is in keeping with its idea while not violating the copyright.

Please let me know how you would solve this problem, knowing that I can only
use the ASCII reprentation for the graphics. Any ideas are most welcome.

Thank you for time, Oplec.
Jul 19 '05 #9

P: n/a
"David White" <no@email.provided> wrote in message
news:Nl******************@nasal.pacific.net.au...
Yes, I've seen these as example OO designs as well, but they are usually
designs, not working implementations. I just don't think a draw() with no
parameters is right for a real graphics system. The Shape objects should be independent of the device because it is not only possible, but highly
likely, that you will sometimes want to print whatever you've drawn on the
screen.
If I had were to have free reign over the design, I would most likely
implement a Surface class that Shapes draw to. My only question would then
be whether or not such a Surface should provide the members for drawing to
it, such as put_pixel(), or whether another class should be implemented to
do so. Any ideas?
Probably. I think the shapes are defined so that the n(), s() etc. members
are sufficient for the window to draw any shape. This is probably a good
enough design, or even the best design, for a system with these limitations. Remember it is just an exercise and not necessarily a proposal for the best design for graphics systems in general. For high resolution graphics systems you will want to have dotted or dashed lines, thick or thin lines, curved
lines, ellipses, pear shapes, closed shapes with various kinds of shading
etc. The possibilities and complexities are endless, and in my opinion the
shape object will usually best know how to draw itself. The type of device
might make a difference too (e.g., raster or non-raster), but the shape
object is where most of the knowledge belongs. So, for this exercise
Stroustrup's design is sufficient, but for more complex drawing I don't
think it is.
From my understanding of the problem, and how I implemented it, the
Points returned by the n(), e(), ..., members could not be used to draw a
shape unless Window knew of what shape the object being drawn was, such as
Line. That would violate the whole idea of what Mr. Stroustrup wrote in the
chapter; He seems to detest casts.
No, I think he's using non-const parameters because there's no need to make them const and because it's the convention.


If it is the convention then I will follow it. Thank you for informing
me of this matter since I was not expecting any help on my code, but it is
most welcome.

Thank you for your time, Oplec.
Jul 19 '05 #10

P: n/a
"jeffc" <no****@nowhere.com> wrote in message
news:3f********@news1.prserv.net...
If you still have questions, why don't you ask him personally? You would
want to distill the message down to be as succinct as possible because I'm
sure he's very busy. He has helped me when I couldn't find a complete
answer here, and in your case you're asking something directly related to
his book.


Quite oftern I visit his website and read his papers. He has a FAQ that
states that there is a good chance that homework problems would not get
answered, and that is why I had not attempted to contact him through e-mail.
I assumed that he would believe my attempt at contact to be solely to obtain
an solution to a problem that I had not solved. Seeing as I did solve it, I
could, perhaps, be quick to indicate that I had solved it. That might
increase my chances of a response.

In actuality, I will probably not contact him because I respect him too much
to waste his time on what is a trivial matter to him.

Thanks for your time, Oplec.
Jul 19 '05 #11

P: n/a
"jeffc" <no****@nowhere.com> wrote in message
news:3f********@news1.prserv.net...
Since you explain it that way, your technique is valid. Nonetheless,
programmers very rarely code this way. Some programmers have never seen it, and for some reason it looks so odd to them that they might think it's a
bug, or at least go off in search of the answer. Therefore, from a
maintenance perspective, it might be considered "confusing". But a
programmer's misunderstanding of a properly working language feature doesn't mean you shouldn't use it. There's nothing wrong with it. You might want
to make a comment as to your intent, if others read your code.


Mr. White explained that it is the convention to not do so, and so I
will go along with such a convention because I am new to C++ and there is
probably a excellent reason for its justification. My using "const" on
parameters passed by value was to assist me when programming the definitions
of functions. When I first began, my functions were often very long and
confusing and having the parameters be "const" was a method I adopted to
combat erroneously changing the parameters well into my definitions. When I
was in the process of solving an exercise, where I was to write a sort
routine for my own doubly linked-list, I ran into much difficulty as a
result of accidentally modifing the passed pointer parameters. I wrote many
versions of the sort algorithm because I had to think about a way to sort
things myself and implement it. When implementing it I soon found out that I
had not fully memorized the operator precedence levels and left-right
associativity rules. I first started using "const" on pointers and
references. I soon, thereafter, used it on value types because I encountered
similar difficultys with accidentally modifying arguments.

I'll comply with convention.

Thanks for your time, Oplec.
Jul 19 '05 #12

P: n/a
Oplec wrote:
I first started using "const" on pointers and
references. I soon, thereafter, used it on value types because I encountered
similar difficultys with accidentally modifying arguments.

I'll comply with convention.


I have to say that f(const int) tends to jar my
sensibilities, but some colleagues use the idiom for the
same reasons that you have expressed.

Personally I've never quite understood why it is that if
they are concerned about not modifying the argument they
don't simply refrain from doing so and assert just before
the function returns that the argument retains its original
value.

Jul 19 '05 #13

P: n/a
David White wrote:


Yes, I've seen these as example OO designs as well, but they are usually
designs, not working implementations. I just don't think a draw() with no
parameters is right for a real graphics system. The Shape objects should be
independent of the device because it is not only possible, but highly
likely, that you will sometimes want to print whatever you've drawn on the
screen.


We separate drawing into other objects. For example a Line,
Circle, or Bezier curve, are geometrical objects that have
mathematical properties which have nothing to do with
drawing. They can be used in applications that do not
involve drawing at all. Applications that need to draw
geometrical objects (GO) attach the GOs to geometrical
drawing objects.

Jul 19 '05 #14

P: n/a
lilburne <li******@godzilla.net> wrote in message
news:bn************@ID-203936.news.uni-berlin.de...
David White wrote:


Yes, I've seen these as example OO designs as well, but they are usually
designs, not working implementations. I just don't think a draw() with no parameters is right for a real graphics system. The Shape objects should be independent of the device because it is not only possible, but highly
likely, that you will sometimes want to print whatever you've drawn on the screen.


We separate drawing into other objects. For example a Line,
Circle, or Bezier curve, are geometrical objects that have
mathematical properties which have nothing to do with
drawing. They can be used in applications that do not
involve drawing at all. Applications that need to draw
geometrical objects (GO) attach the GOs to geometrical
drawing objects.


I was referring only to drawable objects. I wasn't suggesting that a Shape
hierarchy intended for mathematical use should know how to draw.

DW


Jul 19 '05 #15

P: n/a
David White wrote:
lilburne <li******@godzilla.net> wrote in message
news:bn************@ID-203936.news.uni-berlin.de...
David White wrote:


Yes, I've seen these as example OO designs as well, but they are usually
designs, not working implementations. I just don't think a draw() with
no
parameters is right for a real graphics system. The Shape objects should
be
independent of the device because it is not only possible, but highly
likely, that you will sometimes want to print whatever you've drawn on
the
screen.


We separate drawing into other objects. For example a Line,
Circle, or Bezier curve, are geometrical objects that have
mathematical properties which have nothing to do with
drawing. They can be used in applications that do not
involve drawing at all. Applications that need to draw
geometrical objects (GO) attach the GOs to geometrical
drawing objects.

I was referring only to drawable objects. I wasn't suggesting that a Shape
hierarchy intended for mathematical use should know how to draw.


I wasn't criticizing, just presenting another way of
arranging this sort of thing. The geometry example was just
that and example. None of our domain objects, whether they
be curves, surfaces, machines, or material, 'know' about
drawing.
Jul 19 '05 #16

P: n/a
Oplec <an*******@anonymous.com> wrote in message
news:Ka*********************@news04.bloor.is.net.c able.rogers.com...
"David White" <no@email.provided> wrote in message
news:Nl******************@nasal.pacific.net.au...
Yes, I've seen these as example OO designs as well, but they are usually
designs, not working implementations. I just don't think a draw() with no parameters is right for a real graphics system. The Shape objects should be
independent of the device because it is not only possible, but highly
likely, that you will sometimes want to print whatever you've drawn on the screen.


If I had were to have free reign over the design, I would most likely
implement a Surface class that Shapes draw to. My only question would then
be whether or not such a Surface should provide the members for drawing to
it, such as put_pixel(), or whether another class should be implemented to
do so. Any ideas?


I think the Surface class would have basic low-level drawing functions. I
have designed and implemented a graphics system for Windows before. A used a
Shape hierarchy like this (though I called the base class GraphObject), and
my draw function took a GraphContext object. The GraphContext object did
resource management for the graphical objects (selected/deselected pens,
brushes etc.) and was a wrapper for a Windows device context (or CDC
object). (I didn't want my drawable objects to be Windows-specific.) The
drawable objects drew themselves on the GraphContext using the sort of basic
drawing operations that the device context has (draw line, for example). The
GraphContext then drew on the CDC object itself. To draw on a different
surface you simply pass to the draw function a GraphContext that was a
wrapper for a different DC object (printer DC etc.).
Probably. I think the shapes are defined so that the n(), s() etc. members are sufficient for the window to draw any shape. This is probably a good
enough design, or even the best design, for a system with these

limitations.
Remember it is just an exercise and not necessarily a proposal for the

best
design for graphics systems in general. For high resolution graphics

systems
you will want to have dotted or dashed lines, thick or thin lines, curved lines, ellipses, pear shapes, closed shapes with various kinds of shading etc. The possibilities and complexities are endless, and in my opinion the shape object will usually best know how to draw itself. The type of device might make a difference too (e.g., raster or non-raster), but the shape
object is where most of the knowledge belongs. So, for this exercise
Stroustrup's design is sufficient, but for more complex drawing I don't
think it is.


From my understanding of the problem, and how I implemented it, the
Points returned by the n(), e(), ..., members could not be used to draw a
shape unless Window knew of what shape the object being drawn was, such as
Line. That would violate the whole idea of what Mr. Stroustrup wrote in

the chapter; He seems to detest casts.


I've read the exercise myself (in the 3rd edition) and it's not clear to me
what you are supposed to do. I don't understand what object is supposed to
do the drawing, and I don't understand what the attributes of the Shape
objects are. It says that a Line consists of two Points. But further down it
says that a Line is also a Shape, and that a Shape has 9 "contact" points
(whatever that means): e (east), w (west), c(center) etc. And take this
passage: "For example, Line(x.c(), y.nw()) creates a line from x's centre to
y's top left corner." What are x and y here? Are they other Shape objects? I
don't understand the exercise at all.

DW

Jul 19 '05 #17

P: n/a
On Thu, 23 Oct 2003 09:10:11 +1000, "David White" <no@email.provided> wrote:
I've read the exercise myself (in the 3rd edition) and it's not clear to me
what you are supposed to do. I don't understand what object is supposed to
do the drawing, and I don't understand what the attributes of the Shape
objects are. It says that a Line consists of two Points. But further down it
says that a Line is also a Shape, and that a Shape has 9 "contact" points
(whatever that means): e (east), w (west), c(center) etc. And take this
passage: "For example, Line(x.c(), y.nw()) creates a line from x's centre to
y's top left corner." What are x and y here? Are they other Shape objects? I
don't understand the exercise at all.


I haven't read the exercise but it seems very clear what that's all about.
A line is defined by its two endpoints. It has an implicit bounding rectangle
with nine contact points: the corners of the rectangle, the rectangle side
midpoints, and the rectangle centre.

What's a bit confusing is what e.g. the centre of an arc should be.

But I think it would just be wise to distinguish between shape centre and
line midpoint, even though they're the same for a straight line.

Jul 19 '05 #18

P: n/a
Oplec <an*******@anonymous.com> wrote in message
news:ka****************@twister01.bloor.is.net.cab le.rogers.com...

"David White" <no@email.provided> wrote in message
news:O7******************@nasal.pacific.net.au...
I've read the exercise myself (in the 3rd edition) and it's not clear to me
what you are supposed to do. I don't understand what object is supposed to do the drawing, and I don't understand what the attributes of the Shape
objects are. It says that a Line consists of two Points. But further down it
says that a Line is also a Shape, and that a Shape has 9 "contact"
points (whatever that means): e (east), w (west), c(center) etc. And take this
passage: "For example, Line(x.c(), y.nw()) creates a line from x's

centre to
y's top left corner." What are x and y here? Are they other Shape
objects? I
don't understand the exercise at all.
He means the following:
Point Shape::nw() const;
Point Shape::c() const;
Line::Line(Point, Point);


Yes, I got this much, but the exercise still escapes me. I'm glad you and
A.P.S. understand it, though.

I'm also confused over draw(). As you pointed out in your first post,
elsewhere in the book his Shape classes have a draw() member, but this
exercise has a Window::draw and does not mention Shape::draw.
I believe that I understand the problem, but I'm not satisfied with my
solution. My programs seems fine and correctly outputs the simple child's
drawing of a house. When the program is run, you can see the drawing in
ASCII. It uses only Standard C++.

I read that I can attach files that are under 1 MB in size, and so I am
attaching my solution to the problem to this post. The program may be
compiled and you will see the drawing of the house and how it conforms with what Mr. Stroustrup set as criteria. However, I am not satisfied that I
found the particular solution he would write himself under such

constraints.

As I don't even understand the exercise, I don't think I can comment any
more than I have.

BTW, attachments don't really bother me, but in general they don't go down
well in non-binary newsgroups. Most people prefer that you just include the
code in your post.

DW

Jul 19 '05 #19

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bn************@ID-203936.news.uni-berlin.de...

I have to say that f(const int) tends to jar my
sensibilities, but some colleagues use the idiom for the
same reasons that you have expressed.

Personally I've never quite understood why it is that if
they are concerned about not modifying the argument they
don't simply refrain from doing so and assert just before
the function returns that the argument retains its original
value.


A lot of people tend to agree with you, but I really don't understand this
view, as it seems inconsistent to me. Of all the crazy things you can (and
can not) do in C++, programmers pick out this simple one to "not get".
Regarding your comment "simply refrain from doing so" - well, why ever use
const? Why not simply refrain from not changing things that shouldn't be
changed? For that matter, why not simply refrain from writing code with any
bugs in it?
Jul 19 '05 #20

P: n/a
jeffc wrote:
"lilburne" <li******@godzilla.net> wrote in message
news:bn************@ID-203936.news.uni-berlin.de...
I have to say that f(const int) tends to jar my
sensibilities, but some colleagues use the idiom for the
same reasons that you have expressed.

Personally I've never quite understood why it is that if
they are concerned about not modifying the argument they
don't simply refrain from doing so and assert just before
the function returns that the argument retains its original
value.

A lot of people tend to agree with you, but I really don't understand this
view, as it seems inconsistent to me. Of all the crazy things you can (and
can not) do in C++, programmers pick out this simple one to "not get".


If they did it with all built in types that would be fine,
but they don't typically they do somthing like

f(const int age, double weight);

and leave you wondering why 'age' is being treated
specially. When you examine the code you discover that they
had problem writing the method because they were modifying
age. If their problem had involved weight then no doubt that
would be const and age would not. What you have is const
being attached to function arguements haphazardly. They
aren't saying this method won't change 'age' they are
leaving debugging details in the class interface.

Jul 19 '05 #21

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bn************@ID-203936.news.uni-berlin.de...

A lot of people tend to agree with you, but I really don't understand this view, as it seems inconsistent to me. Of all the crazy things you can (and can not) do in C++, programmers pick out this simple one to "not get".


If they did it with all built in types that would be fine,
but they don't typically they do somthing like

f(const int age, double weight);

and leave you wondering why 'age' is being treated
specially. When you examine the code you discover that they
had problem writing the method because they were modifying
age. If their problem had involved weight then no doubt that
would be const and age would not. What you have is const
being attached to function arguements haphazardly.


But to me, it is the "haphazard" that is the problem, not making the
parameter const. I'm sure you've seen something like
void f() const;
void f();
together, when in fact both of them are const functions. But no one ever
complains about this being confusing. Even in C++ FAQs, they recommend
making all appropriate functions const, but they recommend against const
value parameters because "it's confusing". It just seems odd and
inconsistent to me.
Jul 19 '05 #22

P: n/a
jeffc wrote:
"lilburne" <li******@godzilla.net> wrote in message
news:bn************@ID-203936.news.uni-berlin.de...
A lot of people tend to agree with you, but I really don't understand
this
view, as it seems inconsistent to me. Of all the crazy things you can
(and
can not) do in C++, programmers pick out this simple one to "not get".
If they did it with all built in types that would be fine,
but they don't typically they do somthing like

f(const int age, double weight);

and leave you wondering why 'age' is being treated
specially. When you examine the code you discover that they
had problem writing the method because they were modifying
age. If their problem had involved weight then no doubt that
would be const and age would not. What you have is const
being attached to function arguements haphazardly.

But to me, it is the "haphazard" that is the problem, not making the
parameter const. I'm sure you've seen something like
void f() const;
void f();
together, when in fact both of them are const functions. But no one ever
complains about this being confusing.


Well there are two issues there. I'll insist that const
member functions are declared const, and will raise a
software defect notice on any that aren't.

Neither do I like methods that are overriden on const alone.
What you have is an example of a 'candy interface' (Maguire
- Writing Solid Code). I know that it is often done, and
mostly it doesn't hurt, but it can cause problems. At one
point we needed to mark certain objects as having been
modified when mutable methods were called on them. Because
which of the two methods are called depends on the object's
constness retrofitting the system was a costly trawl. We now
discourage the practice and insist that the methods be given
different names.
Even in C++ FAQs, they recommend
making all appropriate functions const, but they recommend against const
value parameters because "it's confusing". It just seems odd and
inconsistent to me.


Anyone coming from a C background knows that if something is
passed by value and the function called won't change it
externally. Therefore to see something like 'const int' in a
function definition leads you to expect a pointer or
reference, that there isn't one makes you think 'perhaps
someone has missed it off'. Its the same as when you see

f(someClass a);

an immediate question forms of why isn't this being passed
by reference.

Jul 19 '05 #23

P: n/a
Would any of you mind offering a design for implementing 12.7[2] that
complys with Mr. Stroustrups constraints? I have provided the source code to
my particular solution in hopes that it might be used as a basis for a quick
solution that is more proper than my own.

Thanks, Oplec.
Jul 19 '05 #24

P: n/a
Oplec <an*******@anonymous.com> wrote in message
news:Iq********************@news04.bloor.is.net.ca ble.rogers.com...
Would any of you mind offering a design for implementing 12.7[2] that
complys with Mr. Stroustrups constraints? I have provided the source code to my particular solution in hopes that it might be used as a basis for a quick solution that is more proper than my own.


I read the exercise again and it made more sense. I was getting hung up on
the purpose of the contact points for something like a line drawn at an
angle. A colleague suggested that the contact points could be the points you
would "grab" in a drawing editor to modify the shape. (The same colleague
then complained that I "always have to understand every detail". Apparently,
I ask too many questions instead of just accepting what's written.)

Although the contact points seem to overly complicate the line class for
this exercise, they are useful for a rectangle, since you will have other
shapes attached to the corners when drawing something like a house.

I've had a look at your code, and it's fine. The reason the exercise had a
Window::draw became clear when I saw your code, i.e., that the window needs
to keep track of its current position by using the se() point of the shape.
Alternatively, the shape could pass its se() to the window instead after
drawing. You could probably have an argument over which class has the
responsibility for doing this, but it's certainly simpler having the window
do it.

The window's then calling the shape's draw by passing a reference to itself
is how I'd envisaged it. Really, there's no other reasonable choice once
you've called w.draw(shape).

I can't find anything to criticize in your design. It's as good as it can be
in my opinion. I can't see anything much wrong with the implementation
either. One thing I'd suggest is making Shape a class rather than a struct.
Although the struct works, it is most unconventional to have an abstract
class such as Shape declared as a struct. Structs are usually used only as
POD ("plain old data") types. 'Shape' being a struct would probably confuse
people.

DW

Jul 19 '05 #25

P: n/a
David White <no@email.provided> wrote in message
news:gf******************@nasal.pacific.net.au...
The reason the exercise had a
Window::draw became clear when I saw your code, i.e., that the window needs to keep track of its current position by using the se() point of the shape. Alternatively, the shape could pass its se() to the window instead after
drawing. You could probably have an argument over which class has the
responsibility for doing this, but it's certainly simpler having the window do it.


Actually, it seems far more logical to me for the window's current position
to be the most recently drawn pixel. Why should it be a shape's se() point?
Maybe Stroustrup specified it that way as an excuse to demonstrate virtual
function calls through an abstract class's interface.

DW

Jul 19 '05 #26

P: n/a
"David White" <no@email.provided> wrote in message
news:ws******************@nasal.pacific.net.au...
Actually, it seems far more logical to me for the window's current position to be the most recently drawn pixel. Why should it be a shape's se() point? Maybe Stroustrup specified it that way as an excuse to demonstrate virtual
function calls through an abstract class's interface.


Mr. Stroustrup has his readers extended previous exercises throughout his
book. This allows for the reader to learn how to implement code that can be
easily extended and also for him to use more advanced exercises that
wouldn't be possible if users didn't already know about linked-list or have
one that they understood. So, for instance, he has his user implement a
linked list in one exercise using just modules, then in another they are to
implement those modules as a class, then they are to add a sort member. Do
those exercises was very beneficial for my learning the language and they
are a further motivation to continue to anwer and solve all of the
exercises. The calculator exercises were a lot of fun to do, and I'm so
amazed that I actually understood it since I have not programmed before.

Since nobody has so far indicated as to how I could improve my solution
further, besides the use of "const int" and "shape&", which I have altered,
I suppose I will now continue the other exercises.

Thank you all for your time and assistance, Oplec.
Jul 19 '05 #27

This discussion thread is closed

Replies have been disabled for this discussion.