Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > designing classes without default c'tor; using them with STLcontainers and operator>>(istream&)

Reply
Thread Tools

designing classes without default c'tor; using them with STLcontainers and operator>>(istream&)

 
 
[rob desbois]
Guest
Posts: n/a
 
      02-01-2008
Hi,

I want a class whose invariant is that the contained data members are
valid - the constructor establishes this invariant, methods assume it
as their precondition and maintain it as their postcondition.
This requires the class to NOT have a default constructor.

I need to store instances of this class in STL containers, which
unfortunately requires that the class has a default constructor. The
obvious solution here is that I have to implement a default
constructor which leaves the class in an invalid state, although this
unfortunately means that every method now has to check the invariant
as a precondition to ensure that it's not vulnerable to class users
who might use the default constructor and not read the documentation.

Another requirement is that I need to implement (non-member) I/O
streaming functions. The output operator<<() is no problem, but again
with an input operator:
istream& operator>>(istream& in, const Foo& f);
I have to use this like so:
Foo f;
in >> f;
This obviously requires, again, that I can construct an instance
through the default constructor thus generating an invalid object.

Is there some great guideline for designing that everyone else knows
and I don't? Is this one of those 'pain-in-the-backside' things that
we just have to put up with and end up implementing default
constructors and invariant checks just so we can do the other things
we need to do?

Thanks for any help anyone can offer...
--rob
 
Reply With Quote
 
 
 
 
Jim Langston
Guest
Posts: n/a
 
      02-01-2008
[rob desbois] wrote:
> Hi,
>
> I want a class whose invariant is that the contained data members are
> valid - the constructor establishes this invariant, methods assume it
> as their precondition and maintain it as their postcondition.
> This requires the class to NOT have a default constructor.
>
> I need to store instances of this class in STL containers, which
> unfortunately requires that the class has a default constructor. The
> obvious solution here is that I have to implement a default
> constructor which leaves the class in an invalid state, although this
> unfortunately means that every method now has to check the invariant
> as a precondition to ensure that it's not vulnerable to class users
> who might use the default constructor and not read the documentation.


One thing you could do is only store pointers to the class in standard
containers.

> Another requirement is that I need to implement (non-member) I/O
> streaming functions. The output operator<<() is no problem, but again
> with an input operator:
> istream& operator>>(istream& in, const Foo& f);
> I have to use this like so:
> Foo f;
> in >> f;
> This obviously requires, again, that I can construct an instance
> through the default constructor thus generating an invalid object.


Consider:
Foo f(in);

That is, a constructor that accepts an istream&

> Is there some great guideline for designing that everyone else knows
> and I don't? Is this one of those 'pain-in-the-backside' things that
> we just have to put up with and end up implementing default
> constructors and invariant checks just so we can do the other things
> we need to do?


There are work arounds as shown, and probably others.

--
Jim Langston
http://www.velocityreviews.com/forums/(E-Mail Removed)


 
Reply With Quote
 
 
 
 
[rob desbois]
Guest
Posts: n/a
 
      02-01-2008
> > I need to store instances of this class in STL containers, which
> > unfortunately requires that the class has a default constructor. The
> > obvious solution here is that I have to implement a default
> > constructor which leaves the class in an invalid state, although this
> > unfortunately means that every method now has to check the invariant
> > as a precondition to ensure that it's not vulnerable to class users
> > who might use the default constructor and not read the documentation.

>
> One thing you could do is only store pointers to the class in standard
> containers.


Thank you - that's a good workaround that hadn't even occurred to me!
I'll look into it.

> > Another requirement is that I need to implement (non-member) I/O
> > streaming functions. The output operator<<() is no problem, but again
> > with an input operator:
> > istream& operator>>(istream& in, const Foo& f);
> > I have to use this like so:
> > Foo f;
> > in >> f;
> > This obviously requires, again, that I can construct an instance
> > through the default constructor thus generating an invalid object.

>
> Consider:
> Foo f(in);
>
> That is, a constructor that accepts an istream&


Another good workaround that occurred to me about 5 minutes after my
initial post.
Seems a shame to give up the nice syntax afforded by the extraction
operator, but I guess I can't have my cake and eat it!

Cheers,
--rob
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-01-2008
* [rob desbois]:
>
> I want a class whose invariant is that the contained data members are
> valid - the constructor establishes this invariant, methods assume it
> as their precondition and maintain it as their postcondition.
> This requires the class to NOT have a default constructor.
>
> I need to store instances of this class in STL containers, which
> unfortunately requires that the class has a default constructor.


No, that isn't a general requirement.

The class must be copy-constructible and assignable in order to have
instances directly in STL containers.

Some /operations/ have an additional requirement of
default-constructible, e.g. std::vector::resize,

#include <string>
#include <vector>

struct Foo
{
std::string value;
Foo( char const s[] ): value( s ) {}
};

int main()
{
using namespace std;

vector<Foo> v; // OK

v.push_back( "blah blah" ); // OK
//v.resize( 52 ); // Req. default construction.
}



> The
> obvious solution here is that I have to implement a default
> constructor which leaves the class in an invalid state, although this
> unfortunately means that every method now has to check the invariant
> as a precondition to ensure that it's not vulnerable to class users
> who might use the default constructor and not read the documentation.


I don't find that obvious at all; in fact introducing a zombie state is
(usually, and here) a sure sign you're on the entirely wrong track.


> Another requirement is that I need to implement (non-member) I/O
> streaming functions. The output operator<<() is no problem, but again
> with an input operator:
> istream& operator>>(istream& in, const Foo& f);
> I have to use this like so:
> Foo f;
> in >> f;
> This obviously requires, again, that I can construct an instance
> through the default constructor thus generating an invalid object.


Why do you have to use it like that?

E.g. why not

FooData data;
in >> fooData;

// Possibly rendundantly checking fooData here just to have control.
// Then

Foo const f( fooData );

More generally, think about moving that i/o and possibly serialization
stuff out of the class proper. Separate concerns. The above is just
one possibility.


> Is there some great guideline for designing that everyone else knows
> and I don't? Is this one of those 'pain-in-the-backside' things that
> we just have to put up with and end up implementing default
> constructors and invariant checks just so we can do the other things
> we need to do?


No, and no.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
[rob desbois]
Guest
Posts: n/a
 
      02-01-2008
> > I need to store instances of this class in STL containers, which
> > unfortunately requires that the class has a default constructor.

>
> No, that isn't a general requirement.


Oh!

> The class must be copy-constructible and assignable in order to have
> instances directly in STL containers.
>
> Some /operations/ have an additional requirement of
> default-constructible, e.g. std::vector::resize,


Hmm...I could've sworn that I'd found a list of requirements and
default-constructability was one of them.
I'm using this class as the value_type of std::map, whose operator[]
implementation requires default construction.
It might be better to use insert() and make_pair() instead of a zombie
state.

> > The
> > obvious solution here is that I have to implement a default
> > constructor which leaves the class in an invalid state, although this
> > unfortunately means that every method now has to check the invariant
> > as a precondition to ensure that it's not vulnerable to class users
> > who might use the default constructor and not read the documentation.

>
> I don't find that obvious at all; in fact introducing a zombie state is
> (usually, and here) a sure sign you're on the entirely wrong track.


Thanks - that was my opinion!

> > Another requirement is that I need to implement (non-member) I/O
> > streaming functions. The output operator<<() is no problem, but again
> > with an input operator:
> > istream& operator>>(istream& in, const Foo& f);
> > I have to use this like so:
> > Foo f;
> > in >> f;
> > This obviously requires, again, that I can construct an instance
> > through the default constructor thus generating an invalid object.

>
> Why do you have to use it like that?
>
> E.g. why not
>
> FooData data;
> in >> fooData;
>
> // Possibly rendundantly checking fooData here just to have control.
> // Then
>
> Foo const f( fooData );
>
> More generally, think about moving that i/o and possibly serialization
> stuff out of the class proper. Separate concerns. The above is just
> one possibility.


Ok, thank you. Separation of concerns is lacking in my current design;
I'll split things up a little more so that the class is responsible
for its contained data only and move I/O concerns elsewhere.

> > Is there some great guideline for designing that everyone else knows
> > and I don't? Is this one of those 'pain-in-the-backside' things that
> > we just have to put up with and end up implementing default
> > constructors and invariant checks just so we can do the other things
> > we need to do?

>
> No, and no.


Damn, and good!

> Cheers, & hth.,


Very much, thank you
--rob
 
Reply With Quote
 
jkherciueh@gmx.net
Guest
Posts: n/a
 
      02-01-2008
[rob desbois] wrote:
[snip]
>
>> > Another requirement is that I need to implement (non-member) I/O
>> > streaming functions. The output operator<<() is no problem, but again
>> > with an input operator:
>> > istream& operator>>(istream& in, const Foo& f);
>> > I have to use this like so:
>> > Foo f;
>> > in >> f;
>> > This obviously requires, again, that I can construct an instance
>> > through the default constructor thus generating an invalid object.

>>
>> Consider:
>> Foo f(in);
>>
>> That is, a constructor that accepts an istream&

>
> Another good workaround that occurred to me about 5 minutes after my
> initial post.
> Seems a shame to give up the nice syntax afforded by the extraction
> operator, but I guess I can't have my cake and eat it!


Huh? If you have a constructor from an istream, you could do:

istream& operator>> ( istream & istr, Foo & foo ) {
foo = Foo(istr);
return ( istr );
}

or

istream& operator>> ( istream & istr, Foo & foo ) {
Foo dummy ( istr );
swap( foo, dummy );
return ( istr );
}

or, in case you don't want extraction to throw, something like:

istream& operator>> ( istream & istr, Foo & foo ) {
try {
Foo dummy ( istr ); // could throw.
swap( foo, dummy ); // should not throw.
}
catch (...) {
// set whatever failure indicating bits you want in istr.
}
return ( istr );
}


Best

Kai-Uwe Bux
 
Reply With Quote
 
[rob desbois]
Guest
Posts: n/a
 
      02-01-2008
On Feb 1, 12:58 pm, (E-Mail Removed) wrote:
> >> > Another requirement is that I need to implement (non-member) I/O
> >> > streaming functions. The output operator<<() is no problem, but again
> >> > with an input operator:
> >> > istream& operator>>(istream& in, const Foo& f);
> >> > I have to use this like so:
> >> > Foo f;
> >> > in >> f;
> >> > This obviously requires, again, that I can construct an instance
> >> > through the default constructor thus generating an invalid object.

>
> >> Consider:
> >> Foo f(in);

>
> >> That is, a constructor that accepts an istream&

>
> > Another good workaround that occurred to me about 5 minutes after my
> > initial post.
> > Seems a shame to give up the nice syntax afforded by the extraction
> > operator, but I guess I can't have my cake and eat it!

>
> Huh? If you have a constructor from an istream, you could do:
>
> istream& operator>> ( istream & istr, Foo & foo ) {
> foo = Foo(istr);
> return ( istr );
> }
>
> or
>
> istream& operator>> ( istream & istr, Foo & foo ) {
> Foo dummy ( istr );
> swap( foo, dummy );
> return ( istr );
> }
>
> or, in case you don't want extraction to throw, something like:
>
> istream& operator>> ( istream & istr, Foo & foo ) {
> try {
> Foo dummy ( istr ); // could throw.
> swap( foo, dummy ); // should not throw.
> }
> catch (...) {
> // set whatever failure indicating bits you want in istr.
> }
> return ( istr );
> }
>


I cannot - implementing operator>>(istream&, Foo&) requires that I
pass it a reference to an existing Foo object. I don't want Foo to be
a default-constructible class, so I'd have to pass it a valid object.
--rob
 
Reply With Quote
 
jkherciueh@gmx.net
Guest
Posts: n/a
 
      02-01-2008
[rob desbois] wrote:

> On Feb 1, 12:58 pm, (E-Mail Removed) wrote:
>> >> > Another requirement is that I need to implement (non-member) I/O
>> >> > streaming functions. The output operator<<() is no problem, but
>> >> > again with an input operator:
>> >> > istream& operator>>(istream& in, const Foo& f);
>> >> > I have to use this like so:
>> >> > Foo f;
>> >> > in >> f;
>> >> > This obviously requires, again, that I can construct an instance
>> >> > through the default constructor thus generating an invalid object.

>>
>> >> Consider:
>> >> Foo f(in);

>>
>> >> That is, a constructor that accepts an istream&

>>
>> > Another good workaround that occurred to me about 5 minutes after my
>> > initial post.
>> > Seems a shame to give up the nice syntax afforded by the extraction
>> > operator, but I guess I can't have my cake and eat it!

>>
>> Huh? If you have a constructor from an istream, you could do:
>>
>> istream& operator>> ( istream & istr, Foo & foo ) {
>> foo = Foo(istr);
>> return ( istr );
>> }
>>
>> or
>>
>> istream& operator>> ( istream & istr, Foo & foo ) {
>> Foo dummy ( istr );
>> swap( foo, dummy );
>> return ( istr );
>> }
>>
>> or, in case you don't want extraction to throw, something like:
>>
>> istream& operator>> ( istream & istr, Foo & foo ) {
>> try {
>> Foo dummy ( istr ); // could throw.
>> swap( foo, dummy ); // should not throw.
>> }
>> catch (...) {
>> // set whatever failure indicating bits you want in istr.
>> }
>> return ( istr );
>> }
>>

>
> I cannot - implementing operator>>(istream&, Foo&) requires that I
> pass it a reference to an existing Foo object. I don't want Foo to be
> a default-constructible class, so I'd have to pass it a valid object.


And what exactly would be the problem?

I think, you are confusing two unrelated issues. Of course you can implement
operator>>, and you could use it. E.g.:

Foo my_object ( some parameters );
... // do stuff with the object.
istr >> my_object; // read a new value into my_object
... // do more stuff

or

Foo my_object ( istr );
do {
...;
} while ( istr >> my_object );

Whether Foo is default constructible or not does no impact upon the
semantics of operator>>. But it might make it tricky to get a valid object
to begin with. Once you have one, you can use operator>> to change its
value.


On the other hand, why would it difficult for a default constructor to
create a valid object? Could it be that your class just lacks a "natural"
default value? In that case, you might consider picking a not so natural
default value. I never found that to be a problem.


Best

Kai-Uwe Bux
 
Reply With Quote
 
[rob desbois]
Guest
Posts: n/a
 
      02-01-2008
> And what exactly would be the problem?

This line is the problem:
Foo f;
I cannot and do not want to implement a default constructor for my
class.

> I think, you are confusing two unrelated issues. Of course you can implement
> operator>>, and you could use it. E.g.:

operator>>() requires an *existing* object to operate on. I could use
it, but I'd have to manually construct a valid object purely to
satisfy the requirement.

> Whether Foo is default constructible or not does no impact upon the
> semantics of operator>>. But it might make it tricky to get a valid object
> to begin with. Once you have one, you can use operator>> to change its
> value.


Indeed - obtaining a valid object *is* the problem here.

> On the other hand, why would it difficult for a default constructor to
> create a valid object? Could it be that your class just lacks a "natural"
> default value? In that case, you might consider picking a not so natural
> default value. I never found that to be a problem.


There is no natural default value for this class which is my I *do not
want* a default constructor; it would make no sense for the class.
I wholeheartedly disagree with picking a not so natural default - that
seems extremely poor class design to me and is like my original
solution - degrading class design purely to satisfy a language
requirement.

--rob
 
Reply With Quote
 
jkherciueh@gmx.net
Guest
Posts: n/a
 
      02-02-2008
[rob desbois] wrote:
[snip]
>> On the other hand, why would it difficult for a default constructor to
>> create a valid object? Could it be that your class just lacks a "natural"
>> default value? In that case, you might consider picking a not so natural
>> default value. I never found that to be a problem.

>
> There is no natural default value for this class which is my I *do not
> want* a default constructor; it would make no sense for the class.
> I wholeheartedly disagree with picking a not so natural default - that
> seems extremely poor class design to me and is like my original
> solution - degrading class design purely to satisfy a language
> requirement.


I won't try to tell you what you should want or should not want. I feel the
need, however, to point out that picking a non-natural value is not
necessarily "extremely poor class design". It is what just about any bignum
class or std::complex does. There is nothing about the number 0 that makes
it "more natural" than 1. So why should std::complex default construct to
0? Well, because it solves problems.

On a related note: many classes do not have a natural comparison.
Nevertheless it does make sense to at least specialize std::less for those
classes that support _some_ ordering so that you can use them hassle-free
in associative containers. Restricting yourself to what feels natural can
get in the way of solving problems.


Best

Kai-Uwe Bux
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Designing lower level classes. Gunter Schelfhout C++ 13 06-30-2008 07:50 AM
how to encapsulate JAI and ImageIO classes into jar file without installing them Java_New Java 1 02-03-2007 07:23 AM
Compacting 5,000 Messages, Moving Them To A Google Account, And Burning Them Onto A CD Martin Computer Support 9 01-14-2007 09:33 PM
Does anyone know them or has any experience with them (good or bad)? Mike Timbell Digital Photography 9 11-13-2003 01:09 AM
Designing presentation layer servlet classes 2BaCook Java 3 11-11-2003 06:47 PM



Advertisments