• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 227
  • Last Modified:

The annoying c++ reference initialization issue.

Hi Experts,

Surely you've all encountered this "reference initialization" problem...

I have a classes Team and Player.  In Team.h I declare five players:

private:
    Player center;
    Player forward1;
    Player forward2;
    ...

Now, in Player.h, I have declared a member reference variable:

private:
    Strategy & default_strategy;

And that's where everything goes to crapola.  The compiler, of course, would like me to initialize default_strategy in Player::Player() : default_strategy = some_strategy { }.  But then I'd have to give every player the same strategy!  In Team.h, the compiler will not allow something like Player center(center_strategy) because it is only the class declaration.  Finally I thought of implementing a member function Player::setStrategy(Strategy &).  But that fails because the comiler _reallY_ wants default_strategy initialized in the default constructor.

AHHH!!!  

Surely there must be a way for one object to have another object as a member variable that has a user-setable member reference variable????

FYI, I'd really like to implment default_strategy as a reference because in my actual code the type is an object inherited from ostream.  And it just makes sense to work with ostream objects as references rather than, say, pointers.  

Also FYI, the setting of default_strategy need not be done at compile time.  That is, I know precisely that I want to set the center's default_strategy to center_strategy, the two forwards' strategies to forward_strategy, etc.

Thanks!

0
highqllc
Asked:
highqllc
  • 7
  • 5
  • 4
  • +1
6 Solutions
 
jkrCommented:
Why not using a pointer here and initializing it to NULL in the constructor?
0
 
highqllcAuthor Commented:
well, in my actual code the object I'd like to get a reference to is an ostream-inhereted object...   so to use it with you pointer suggestion, I'd have to do the equivalent of (*cout) << "blah"  instead of just cout << blah.

Yes, it would work, but is there really no other way???
0
 
jkrCommented:
Well, references must be initialized, there is no way to skip that. If you want to save work when using a pointer, you could add a convenience member that would

ostream& get() { return *m_pStream;}

so you can write

get() << "blah";
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

 
highqllcAuthor Commented:
hmm....  and the only way to do the initialization is like this: Player::Player() : default_strategy(some_strategy) { }?   I'd be happy with another way that allowed me to make use of any type of conditional logic on setting the reference... if(blah) default_strategy = strat1; else default_strategy = strat2;
0
 
jkrCommented:
That you are free to do, e.g.

Player::Player() : default_strategy(blah ? some_strategy : other_strategy) { }

0
 
itsmeandnobodyelseCommented:
>>>> Well, references must be initialized, there is no way to skip that.
You can reference to a object created by new as a pointer.

Player::Player() : default_strategy(*(new Strategy(/*default = */ true))) {}

Later you can update it by a set function.

You get rid of the object created by new in the destructor by

Player::~Player()
{
    ...
    delete &default_strategy;
}

Regards Alex
0
 
highqllcAuthor Commented:
That's an interesting suggestion, Alex.  But how would you update it later with a set function?  The only L-value associated with the newly-created memory is default_strategy.   What would you update?  Also, I didn't understand completely this : /*default = */ true, but assume it was not critical to your suggestion.  

Thanks,

kevin
0
 
itsmeandnobodyelseCommented:
>>>> But how would you update it later with a set function?

void Player::setStrategy(const Strategy& strategy)
{
     default_strategy = strategy;
}

Note, class Strategy needs a operator= for that if it contains member pointers or class members with member pointers (never copy member pointers to different objects but only the contents).

Regards, Alex
0
 
itsmeandnobodyelseCommented:
>>>> /*default = */ true, but assume it was not critical to your suggestion.
Yes, it only should make clear, that Strategy needs different constructors, one of them is the initial (default) strategy.
0
 
itsmeandnobodyelseCommented:
>>>> the object I'd like to get a reference to is an ostream-inhereted object...  

Hmmm. stream class objects are not suitable to hold them as a member in a class. The reason is that they always have a current status which makes it not easy to 'update' a stream object with another stream. Actually, newer versions of the VC compiler forbid to make 'copies' of a stream object by providing a private copy constructor and a private assignment operator. So, the above sample code would not compile with these compilers because of that.

Instead stream objects should be created newly each time they were needed. In case of a file stream alway open and close a new stream object in the functions where you need them. There are only rare cases where that would make any performance issue. If you can't do it, you better pass the stream object by reference to any function where you need it.
0
 
jasonclarkeCommented:
>>>> But how would you update it later with a set function?
>> void Player::setStrategy(const Strategy& strategy)

this looks very error prone to me...  it seems very likely to lead to unintended behaviour... for example:

Strategy s1(...);  
Strategy s2(...);
Team t(s1);  /// create team set default strategy to s1...
t.setStrategy(s2);  // oops... overwrites s1.... possibly unintentional?

it seems to me that where the object has to change storing a reference is not appropriate - either store an object copy or store a pointer...  
0
 
itsmeandnobodyelseCommented:
>>>> t.setStrategy(s2);  // oops... overwrites s1.... possibly unintentional?
No, that doesn't happen.

 t.setStrategy(s2);  

passes a const reference of s2 to Player t. In setStrategy we have a own member object allocated by 'new' and

   default_strategy = strategy;

copies the contents (not the reference) of s2 to the default_strategy member.

That is absolutely safe (actually safer than a pointer would be) .... but won't compile if the Strategy was derived from basic_ostream cause the operator= is a private member of one of the member classes.

Regards, Alex


0
 
jasonclarkeCommented:
> No, that doesn't happen.

maybe it would be too easy to have a constructor that did not behave this way, i.e.:

Team(Strategy& s) : default_strategy(s) {}

where the problem would apply.  

There don't seem to be many benefits to me of storing a reference as a class member like this.  You don't really lose anything much by storing an object by value or using a pointer in the class (Especially if you are creating the object using new - why not just store the pointer?).  


0
 
highqllcAuthor Commented:
ok ok, I've decided to go with a pointer...    it just looks weird in my code to have stuff around like  *p_cout << blah, rather than: cout << blah
0
 
jkrCommented:
>>t just looks weird in my code

As I wrote earlier - you could add a convenience member that would

ostream& get() { return *m_pStream;}

so you can write

get() << "blah";
0
 
itsmeandnobodyelseCommented:
>>>> There don't seem to be many benefits to me of storing a reference
>>>> as a class member like this.
The main benefit is that it is always valid, what is the main problem of pointers. Onother benefit is that you don't need dereference. As the only code for new/delete is in the constructors/destructor it is much safer than the get method jkr posted which will return a invalid reference if the pointer is not valid.

>>>> Team(Strategy& s) : default_strategy(s) {}
Actually that is a unsafe and bad method to initialize a reference member. I suggested

   Player::Player()  : default_strategy(*(new Strategy())) {}

what always would generate a valid member.

>>>>> ok ok, I've decided to go with a pointer...    
???

Actually a pointer solves nothing beside of some minor initialization problems. Not a member pointer which was provided outside of the class is really bad OOP. You always should create a copy of the object the pointer is pointing to if using pointer members at all .... and than you'll have the same copy problem as with a reference member.

Can you tell why the Strategy object needs to be derived from a stream class? If you want top read/write from/to a file, it is always better to have a string member containing the file path, and have a temporary stream object when accessing the file.

Regards, Alex
0
 
highqllcAuthor Commented:
Hi everyone,

Thanks for all the good comments.  I initially thought the answer could be super-simple, but that's clearly not the case.  I'm probably having these issues due to poor design...    My stream objects behave like cout but when you send data to them, they intellegently position the data on the screen and choose which file to copy the data to. For example,

myout_left << "blah"  

would position "blah" on the left hand side of the screen whereas

myout_right << "blah"

would position it on the right hand side.  

My goal was to create a class that had a reference to one of these stream objects so that I could set where it wrote to one level of abstraction up. But based on this thread, I should probably re-think the design.

Anyways, thanks for all the comments,

kevin
0
 
itsmeandnobodyelseCommented:
>>>> But based on this thread, I should probably re-think the design.

Yes, I would like to agree to that although I don't know all features you wanted to implement by using the stream members.

Generally, formatting and positioning best were made in overloads of operator<<. But then you don't have

      myout_left << "blah";

but something like

class Player
{
     ...
    friend std::ostream& operator<< (std::ostream& os, const Player& p)
    {
          int offset = p.getOffset();
          if (default_strategy->isLeftPlayer())
          {
                 os << std::endl;
                 os << std::setw(5) << " " << p.getname();  // offset 5
                 offset = 5;
          }
          else /* if (default_strategy->isRightPlayer()) */
          {
                 os << std::setw(65 - offset) << " " << p.getname();  // offset 65
                 offset =  65;
          }
          p.setOffset(offset + p.getname().length());
          return os;
    }
    ...

and later

    Strategy left(true);
    Strategy right(false);
    Player player1("Joe", left);
    Player player2("Mary", right);
    cout << player1 << player2;               // screen
    outputfile << player1 << player2;        // file

   
Generally, operator<< prints the contents of an object to an arbitrary device (stream). So, if you want to print both  to screen and file you need two statements.

Regards, Alex
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

  • 7
  • 5
  • 4
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now