Re: Creating an object that is read from an input stream.
From: David White (no_at_email.provided)
Date: 12/12/03
- Next message: Chris Theis: "Re: Two templates with overlap."
- Previous message: Walter Kalata: "Re: MSVC7: Comdef.h(266) - missing ')'"
- In reply to: Jason Heyes: "Re: Creating an object that is read from an input stream."
- Next in thread: Cy Edmunds: "Re: Creating an object that is read from an input stream."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 12 Dec 2003 20:22:33 +1100
"Jason Heyes" <geoffhys@optusnet.com.au> wrote in message
news:3fd93bd1$0$1024$afc38c87@news.optusnet.com.au...
> "Cy Edmunds" <cedmunds@spamless.rochester.rr.com> wrote in message
> news:%W1Cb.7559$UY6.701@twister.nyroc.rr.com...
> > This is what I meant in a previous post about not mixing up different
> > concepts. What does a Box have to do with streams? Nothing. Consider:
> >
> > #include <iostream>
> > #include <fstream>
> >
> > class Box
> > {
> > private:
> > int m_left, m_right, m_up, m_down;
> >
> > public:
> > Box() {}
> >
> > Box(int i_left, int i_right, int i_up, int i_down) :
> > m_left(i_left), m_right(i_right), m_up(i_up), m_down(i_down) {}
> >
> > int left() const {return m_left;}
> >
> > int right() const {return m_right;}
> >
> > int up() const {return m_up;}
> >
> > int down() const {return m_down;}
> > };
> >
> > std::istream &
> > operator >> (std::istream &istr, Box &b)
> > {
> > int i_left, i_right, i_up, i_down;
> > istr >> i_left >> i_right >> i_up >> i_down;
> > b = Box(i_left, i_right, i_up, i_down);
> > return istr;
> > }
> >
> > int main()
> > {
> > Box b;
> > std::ifstream ifs("box_numbers.txt");
> > ifs >> b;
> > if (!ifs)
> > {
> > std::cout << "oops\n";
> > return 1;
> > }
> > std::cout << b.left() << ' ' << b.right() << ' '
> > << b.up() << ' ' << b.down() << '\n';
> > }
> >
> > As you can see, in C++ it is easy to separate I/O streams from the objects
> > they work on. This is basically how std::complex works, for instance,
> > although in that case the class was templated and wide character streams
> had
> > to be implemented. Note that the error handling here is just the same as
> it
> > might be with any other stream operation -- the fact that it happens to be
> a
> > Box shouldn't require special treatment.
> >
> > Cy
>
> The Box class has a default constructor that leaves it's members undefined.
> Therefore the Box object created in main is not valid until
>
> ifs >> b;
>
> occurs. If anyone uses the Box object before that line, it will lead to
> unexpected results.
Well, the default constructor can put it into a valid state then.
> I don't see the point of the other constructor.
It's there so you can construct a box with whatever values you want. Is that not useful?
> Why not write:
>
> class Box
> {
> int left, right, up, down;
> public:
> Box() { }
> friend std::istream &operator>>(std::istream &is, Box &box);
> };
>
> std::istream &operator>>(std::istream &is, Box &box)
> {
> return is >> box.left >> box.right >> box.up >> box.down;
> }
>
> This way you don't need the other constructor.
Having it means that you don't even have to make the function a friend, so there is not a single
mention of std::istream cluttering up the Box class. In any case, surely such a constructor is
virtually mandatory for this class, just for general-purpose use without streams.
> You could also write:
>
> class Box
> {
> int left, right, up, down;
> public:
> Box() { }
>
> std::istream &extract(std::istream &is)
> {
> return is >> left >> right >> up >> down;
> }
> };
>
> std::istream &operator>>(std::istream &is, Box &box)
> { return box.extract(is); }
>
>
> The real problem as I see it is to get around the default constructor. You
> can make the default constructor private but that still allows invalid
> objects to exist.
By 'invalid' do you mean uninitialized, or not initialized with the right values (if the default
constructor were to initialize the members to default values)? And why is an "invalid" object a
problem? You are going to make it "valid" in the very next statement after you create it. For
example, is there anything disastrous about either of these?
int value;
is >> value;
Or:
int value = 0;
is >> value;
So what if the object you are reading into isn't the right value for a moment?
> Here is what I was thinking:
>
>
> class Box
> {
> int left, right, up, down;
>
> Box(int left_, int right_, int up_, int down_) :
> left(left_), right(right_), up(up_), down(down_)
> { }
>
> public:
> int get_area() const { return (right - left) * (up - down); }
>
> // reads a new box
> static Box *create(std::istream &is);
>
> std::istream &extract(std::istream &is)
> {
> int left, right, up, down;
> if (!(is >> left >> right >> up >> down))
> return is; // this box is valid even when input fails
> *this = Bar(left, right, up, down);
> return is;
> }
>
> std::ostream &insert(std::ostream &os) const
> { return os << left << right << up << down; }
> };
>
> // reads a new box
> Box *Box::create(std::istream &is)
> {
> int left, right, up, down;
> if (!(is >> left >> right >> up >> down))
> return 0;
> return new Box(left, right, up, down);
> }
>
> // reads into an old box
> std::istream &operator>>(std::istream &is, Box &box)
> { return box.extract(is); }
As I said in my first post (if I remember correctly back that far), why not ditch the extract
function in the class and make this function a friend so it can set the members directly? That
way you don't clutter your Box class with stream stuff.
> std::ostream &operator<<(std::istream &os, const Box &box)
> { return box.insert(os); }
>
> class BoxRef
> {
> SharedPtr<Box> ptr;
>
> public:
> int get_area() const { return ptr->get_area(); }
>
> std::istream &extract(std::istream &is)
> {
> if (ptr) {
> ptr.make_unique();
> return is >> *ptr;
> }
>
> Box *new_ptr = Box::create(is);
> if (!new_ptr)
> return is;
>
> ptr = new_ptr;
> return is;
> }
>
> std::ostream &insert(std::ostream &os) const
> { return os << *ptr; }
> };
>
> std::istream &operator>>(std::istream &is, BoxRef &ref)
> { return ref.extract(is); }
>
> std::ostream &operator<<(std::ostream &os, const BoxRef &ref)
> { return ref.insert(os); }
>
> int main()
> {
> BoxRef mybox;
> if (!(std::cin >> mybox))
> return 1;
> std::cout << mybox.get_area() << std::endl;
> std::cout << mybox << std::endl;
> return 0;
> }
>
>
> At no point in time does an invalid Box object exist. Instead we allow a
> null reference to exist and then we read into it. What do you think?
I think it's extremely and unnecessarily complicated. Apart from the new Box option (which could
easily be added to Cy's version), what are the advantages of your version?
DW
- Next message: Chris Theis: "Re: Two templates with overlap."
- Previous message: Walter Kalata: "Re: MSVC7: Comdef.h(266) - missing ')'"
- In reply to: Jason Heyes: "Re: Creating an object that is read from an input stream."
- Next in thread: Cy Edmunds: "Re: Creating an object that is read from an input stream."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|