Link to home
Start Free TrialLog in
Avatar of highqllc
highqllc

asked on

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!

SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of highqllc
highqllc

ASKER

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???
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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;
That you are free to do, e.g.

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

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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
>>>> 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
>>>> /*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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>>> 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


> 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?).  


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
>>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";
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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
>>>> 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