1

Attempting to post an easier to read/debug example of a question I posted earlier. The A-object in main.cpp, which is passed by reference into a B-object seems to end up being a copy of the original A-object; that is to say operations performed on the A-object within the B-object do not affect the instance of the A-object created in the main.cpp. Given the print commands in the main.cpp, it prints the following: 17, 17, 42, 17; when I would expect the program to print 17, 17, 42, 42.

[main.cpp]
#include <iostream>
#include "A.h"
#include "B.h"
using namespace std;

int main()
{
    A a = A();
    B b = B();

    a.setNumber(17);
    b.setA(a);

    cout << a.getNumber() << endl; //prints 17
    cout << b.getNum() << endl; //prints 17

    b.changeNumber(42); 
    cout << b.getNum() << endl; //prints 42
    cout << a.getNumber(); //prints 17
}


[A.cpp]

void A::setNumber(int num)
{
    number = num;
}

int A::getNumber()
{
    return number;
}


[B.cpp]

void B::setA(A &aObj)
{
    a = aObj;
}

void B::changeNumber(int num)
{
    a.setNumber(num);
}

int B::getNum() {
    return a.getNumber();
}

[[Fields]]
[A.h] int number;
[B.h] A a;

Thank you for reading!

  • B's a field is not a reference, it is a fully-formed object. This is why you see a copy. To make it a reference, either change the type to `A&` and have it assigned in the constructor, or use use [`std::reference_wrapper`](https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper) as the type instead. – Brian Rodriguez Nov 28 '20 at 16:37
  • And.. if you make it a reference, you'll open another problem. a `B` cannot thereafter being constructed without a hard-initialization of that `A` reference in a member-initializer list at construction time (using a constructor you don't have, and a member initializer list you also don't have). References can't dangle like pointers; they require initialization. Something tells me you're about to discover this, sooner rather than later. – WhozCraig Nov 28 '20 at 16:43
  • if this is a fix of your [previous question](https://stackoverflow.com/questions/65051300/how-do-i-correctly-pass-a-reference-of-a-class-object-into-another-class-object) please consider to delete the old one. Actually you could have edited the old one. Already there you were suggested to use a reference wrapper. – 463035818_is_not_an_ai Nov 28 '20 at 16:44
  • @idclev463035818 I deleted the old one, thanks. – Josh Claydon Nov 28 '20 at 16:55
  • @BrianRodriguez Ok i've tried what you first suggested, but I guess im not using the constructor correctly as I'm getting the error 'references must be initialized'. This is how i wrote the constructor: B::B(A aObj) : a{ aObj } { } – Josh Claydon Nov 28 '20 at 16:57
  • @JoshClaydon the constructor's type should also be a reference (`A&`). Note that you can not reassign references, so your `setA` function will not compile. Use `std::reference_wrapper` if you want to reassign them. Learn more about references here: https://stackoverflow.com/a/596750/4859885. – Brian Rodriguez Nov 29 '20 at 00:57

1 Answers1

-1

The member a in B should be some kind of reference or pointer.

Here is a sample implementation with pointer usage in B:

#pragma once

#include "A.h"

class B
{

public:
    void setA(A &aObj)
    {
        a = &aObj;
    }

    void changeNumber(int num)
    {
        a->setNumber(num);
    }

    int getNum()
    {
        return a->getNumber();
    }

private:
    A *a;
};

or written with reference:

#pragma once

#include "A.h"

class B
{

public:
    B(A& obj)
    : a(obj)
    {
    }

    void changeNumber(int num)
    {
        a.setNumber(num);
    }

    int getNum()
    {
        return a.getNumber();
    }

private:
    A& a;
};

Otherwise, if B is not referencing A, it would not recognize the change as it is holding its own copy. (If you use pointers/references be also aware of copy/move behaviour and lifetime of the object you referenced)

  • Creating a pointer to a passed object reference is really dangerous, much more than your warning suggests. You don't have any way to track or enforce that object's lifetime. – Mark Ransom Nov 28 '20 at 16:58
  • Thank you Dirk, this gives the desired result, having tried and failed to implement other suggestions (though this clearly points to a lack of my understanding, rather than bad suggestions) @MarkRansom Are you saying this is not an appropriate solution? Cheers – Josh Claydon Nov 28 '20 at 17:08
  • Thats true, but another story. Passing of life-time-aware types should be prefered. For example a smart-pointer can do the job. Or if possible the objects constructor can take a reference and hold the member as reference as well. – Dirk Hönisch Nov 28 '20 at 17:15
  • Thanks for your help @DirkHönisch, I have marked my question as answered. – Josh Claydon Nov 28 '20 at 17:19
  • Thanks, also edited and added a solution for writting it with a reference. – Dirk Hönisch Nov 28 '20 at 17:21
  • Nice one, I will try that approach too :) – Josh Claydon Nov 28 '20 at 17:22
  • Holding the member as a reference does nothing to avoid the lifetime problem, in fact it helps to hide it. You need to be more careful with the advice you hand out. – Mark Ransom Nov 29 '20 at 04:46
  • I can't see any lifetime problem in the original problem. I also mentioned that it have to be considered. Passing objects as references is a normal thing to do. It all depends on the circumstances. If for example the object was created as local variable in a function and returned as reference it would be a problem. But I can't see the problem with the given code. Please provide some more useful details about your doubts. – Dirk Hönisch Nov 29 '20 at 17:19