0

My c++ is rusty, so while trying to improve some code I wrote a few days ago by changing some calls from passing a MyClass *const thing to Myclass& thing, I noticed that nothing complained about code that followed this contrived example.

#include <iostream>

class Foo {
    public:
        Foo() {
            std::cout << "foo created" << std::endl;
        }
        ~Foo() {
            std::cout << "foo destroyed" << std::endl;
        }
        Foo(Foo& other) {
           member = other.member;
           std::cout << "foo copied" << std::endl;
        }
        bool member = false;
};

class Bar {
    public:
        Bar(Foo& foo) :foo_(foo) { } 
    Foo foo_;  // **** HERE IS THE BUG this should be: Foo& foo_;
};

int main() {
    Foo foo;
    Bar barOne(foo);
    Bar barTwo(foo);
    foo.member = true;
    std::cout << barOne.foo_.member << std::endl;
    std::cout << barTwo.foo_.member << std::endl;
}

I really wanted to have one Foo object, but since I forgot the & I got three instead.

foo created
foo copied
foo copied
0
0
foo destroyed
foo destroyed
foo destroyed

adding the & I get the right result.

foo created
1
1
foo destroyed

Note: the Foo, constructors and destructor are just there to demonstrate what's happening.

I know is legal, but is there a compiler flag that would warn you if you declare an Object as a member variable? Is it a bad practice to store a reference in a member variable? I would not think it is, but like I said my c++ is rusty to say the least.

Update

To answer the question of what I was refactoring from. I was doing something similar to this. I was refactoring to references as everything I read about modern c++ says to prefer references rather than pointers.

class Bar {
    public:
        Bar(Foo const* foo) :foo_(foo) { } 
    Foo const* foo_;
};

int main() {
    Foo foo;
    Bar barOne(&foo);
    Bar barTwo(&foo);
    foo.member = true;
    std::cout << barOne.foo_->member << std::endl;
    std::cout << barTwo.foo_->member << std::endl;
}
nPn
  • 16,254
  • 9
  • 35
  • 58
  • 2
    How should the compiler know if you intended your object to be a reference? For all it knows, you wanted to do a copy. – Rakete1111 Jan 05 '18 at 19:23
  • 1
    _declare an Object as a member variable_ could you clarify this? – default Jan 05 '18 at 19:24
  • You could `Foo(const Foo& other) = delete` to ban all copying, but this may result in other nastiness if you do have cases where you DO want the object copied. – user4581301 Jan 05 '18 at 19:24
  • are you really suggesting a compiler warning for every copy you do in your program? – default Jan 05 '18 at 19:26
  • If you want your member object to be shared but must be initialized and you don't want to check if it's a null or not; then it's a very good practice to use reference as member. – seleciii44 Jan 05 '18 at 19:26
  • If you were using pointers before, what was `Foo::foo_` before you changed to references? Does `Foo` have a `Foo::Foo(Foo*)` constructor? – François Andrieux Jan 05 '18 at 19:30
  • @seleciii44 Using a reference data member is rarely (but not never) a good design decision. The trade-off is that you forgo assignability for your class, which almost always means you also forgo value semantics which is very valuable. Using a pointer comes with the few disadvantages you mentioned, but it's generally worth it. – François Andrieux Jan 05 '18 at 19:33
  • @FrançoisAndrieux I don't see a good reason to use pointer rather than the assignability and ownership of the object. Also, which value semantics do you think you loose compared to pointer? – seleciii44 Jan 05 '18 at 19:50
  • 2
    @seleciii44 Value semantics for the whole class. If *what* you refer to is part of the state or value of the object, than you cannot assign from an instance to another. Sometimes, what is referred is not part of the state, rather the referred object's state is. In those cases it's not as much of a problem. Note that references cannot represent ownership, expect for the case where a local `const` reference extends the lifetime of a temporary, which is not applicable when talking about class data members. – François Andrieux Jan 05 '18 at 19:55
  • @FrançoisAndrieux my bad. I've never thought or come across the problem of assigning objects with a reference member. This also made me realize that i didn't know the [std::reference_wrapper](http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper) which sort of solves the problem. Also [this](https://stackoverflow.com/questions/892133/should-i-prefer-pointers-or-references-in-member-data) covers the problem well. Thanks for pointing it out. – seleciii44 Jan 05 '18 at 20:17
  • @FrançoisAndrieux ok, I understand what you are saying, if I have an instance of Bar, I can't assign a different Bar to it because the reference Foo can not be changed, and this is a compile time fail, right? So in a case were I would definitely not need to do this assignment, would you still prefer the pointer solution? FYI the real use case is more like model reference being held by a controller. I think the only down side of my original solution was the "prefer references over pointers" – nPn Jan 05 '18 at 22:58

2 Answers2

2

I know is legal, but is there a compiler flag that would warn you if you declare an Object as a member variable?

I doubt there is such a flag. Objects of one type are stored as member variables of other types too many times and too many places for that flag to be useful.

Is it a bad practice to store a reference in a member variable?

No, it is not. However, you have to be aware of where you run into problems.

As long the life of the object that holds the reference ends before the life of the object to which it holds the reference ends, you will be fine. Otherwise, you end up holding on to a dangling reference. Using a dangling reference is cause for undefined behavior.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • This I understand, and would also be true if it were a pointer, right? The pointer could become invalid. – nPn Jan 05 '18 at 19:48
  • 1
    @nPn that's right. You should consider using smart pointers if it's a concern for you. – seleciii44 Jan 05 '18 at 19:52
0

Storing object as a member of other object is prefectly fine. Storing reference as a member is OK if you are sure that the object holding the reference is never going to outlive the referenced variable.

StPiere
  • 4,113
  • 15
  • 24