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

Discussion in 'C++' started by Oplec, Oct 22, 2003.

  1. Oplec

    Oplec Guest

    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();
    }
     
    Oplec, Oct 22, 2003
    #1
    1. Advertising

  2. Oplec

    David White Guest

    Oplec <> wrote in message
    news:FMjlb.117789$...
    > 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
     
    David White, Oct 22, 2003
    #2
    1. Advertising

  3. Oplec

    Oplec Guest

    ----- Original Message -----
    From: "David White" <>
    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.
     
    Oplec, Oct 22, 2003
    #3
  4. On Wed, 22 Oct 2003 01:25:48 GMT, "Oplec" <> 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.
     
    Alf P. Steinbach, Oct 22, 2003
    #4
  5. Oplec

    David White Guest

    Oplec <> wrote in message
    news:w0llb.118400$...
    > ----- Original Message -----
    > From: "David White" <>
    > 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
     
    David White, Oct 22, 2003
    #5
  6. Oplec

    jeffc Guest

    "Oplec" <> wrote in message
    news:w0llb.118400$...
    > > 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.
     
    jeffc, Oct 22, 2003
    #6
  7. Oplec

    jeffc Guest

    "David White" <> wrote in message
    news:Nlmlb.6612$...
    >
    > 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.
     
    jeffc, Oct 22, 2003
    #7
  8. Oplec

    jeffc Guest

    "Oplec" <> wrote in message
    news:FMjlb.117789$...
    > 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.
     
    jeffc, Oct 22, 2003
    #8
  9. Oplec

    Oplec Guest

    "Alf P. Steinbach" <> wrote in message
    news:...

    > 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.
     
    Oplec, Oct 22, 2003
    #9
  10. Oplec

    Oplec Guest

    "David White" <> wrote in message
    news:Nlmlb.6612$...

    > 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.
     
    Oplec, Oct 22, 2003
    #10
  11. Oplec

    Oplec Guest

    "jeffc" <> wrote in message
    news:...

    > 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.
     
    Oplec, Oct 22, 2003
    #11
  12. Oplec

    Oplec Guest

    "jeffc" <> wrote in message
    news:...

    > 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.
     
    Oplec, Oct 22, 2003
    #12
  13. Oplec

    lilburne Guest

    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.
     
    lilburne, Oct 22, 2003
    #13
  14. Oplec

    lilburne Guest

    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.
     
    lilburne, Oct 22, 2003
    #14
  15. Oplec

    David White Guest

    lilburne <> wrote in message
    news:bn6sp0$tut43$-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
     
    David White, Oct 22, 2003
    #15
  16. Oplec

    lilburne Guest

    David White wrote:

    > lilburne <> wrote in message
    > news:bn6sp0$tut43$-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.
     
    lilburne, Oct 23, 2003
    #16
  17. Oplec

    David White Guest

    Oplec <> wrote in message
    news:Kaylb.239392$ko%...
    > "David White" <> wrote in message
    > news:Nlmlb.6612$...
    >
    > > 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
     
    David White, Oct 23, 2003
    #17
  18. On Thu, 23 Oct 2003 09:10:11 +1000, "David White" <> 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.
     
    Alf P. Steinbach, Oct 23, 2003
    #18
  19. Oplec

    David White Guest

    Oplec <> wrote in message
    news:kaJlb.3993$...
    >
    > "David White" <> wrote in message
    > news:O7Elb.6682$...
    >
    > > 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
     
    David White, Oct 23, 2003
    #19
  20. Oplec

    jeffc Guest

    "lilburne" <> wrote in message
    news:bn6rmo$tq3ms$-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?
     
    jeffc, Oct 23, 2003
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Antonio Lopez Arredondo
    Replies:
    3
    Views:
    409
  2. Phil Carmody
    Replies:
    13
    Views:
    535
    White Wolf
    Sep 12, 2003
  3. goer
    Replies:
    0
    Views:
    496
  4. John Heitmuller.

    How would you implement graphics in ASP.NET?

    John Heitmuller., Jan 6, 2009, in forum: ASP .Net
    Replies:
    4
    Views:
    770
    John Heitmuller.
    Jan 7, 2009
  5. Antonio Lopez Arredondo

    how would you implement Intellisense experience in ASP.NET pages ???

    Antonio Lopez Arredondo, Nov 8, 2003, in forum: ASP .Net Web Controls
    Replies:
    3
    Views:
    204
Loading...

Share This Page