0

I have 2 classes lets say Class A and Class B,

class A {

public: 
    A(B b);
    B GetB();

private:
    B b;
};

class B {

public:
    B();
    void IncrementCounter();
    int GetCounter();

private:
   int counter = 0;
};

I want to pass an object of type B to class A's constructor and then save this instance of class B in Class A instance. What is the best way to pass class B instance as a parameter, and what is the best way to save class B instance in class A instance.

Note: I do not want to create copies of class B instance, I want A.getB().GetCounter to always be the same as b.GetCounter().

int main(){
   B b;
   A a(b);
   b.IncrementCounter();
   a.getB().IncrementCounter();

   // then b.GetCounter() is same as a.getB().GetCounter() and both = 2

}

I see people using pointers/smart pointer and references/std:reference_wrapper, what is the difference?

heyloo
  • 35
  • 3
  • Reference or smart pointers are the way to go, if you don't want to have copies. – πάντα ῥεῖ Feb 03 '23 at 18:12
  • Constructor initialization list? `A :: A(B new_b) : b(new_b) { ; }` – Thomas Matthews Feb 03 '23 at 18:12
  • @πάνταῥεῖ I dont know which one is better in my case in terms of efficiency – heyloo Feb 03 '23 at 18:14
  • 3
    Forget about efficiency for the moment. Which describes the problem the code is solving better? Make the code easy to understand and worry about efficiency when the profiler tells you it's a problem. – user4581301 Feb 03 '23 at 18:21
  • 1
    The question "which is better?" often leads to a question being closed for being opinionated. Are you asking for an opinion? Or are you asking for some objective measurement? – Drew Dormann Feb 03 '23 at 18:24

2 Answers2

0

Use std::shared_ptr if you don't want copies, example : I assume you are familiar with references, const references and const member functions.

#include <memory>
#include <iostream>

class B
{
public:
    B()
    {
        number_of_instances++; // keep track of number of instances of class B
    }

    void IncrementCounter()
    {
        counter++;
    }

    int GetCounter() const
    {
        return counter;
    }

    int NumberOfInstances() const
    {
        return number_of_instances;
    }

private:
    int counter{ 0 };
    static int number_of_instances;
};

class A 
{
public:
    A(const std::shared_ptr<B>& b) :
        m_b{ b }
    {
    }
    
    // return a reference to the object shared_ptr m_b points to
    B& GetB() 
    {
        return *m_b;
    }

    // return a const reference to the object shared_ptr m_b points to
    const B& GetB() const
    {
        return *m_b;
    }

private:
    // https://en.cppreference.com/w/cpp/memory/shared_ptr
    std::shared_ptr<B> m_b;
};

int B::number_of_instances{ 0 };


int main()
{
    auto b = std::make_shared<B>();
    b->IncrementCounter();

    A a1(b);
    A a2(b);

    std::cout << "number of instances of B = " <<b->NumberOfInstances() << "\n";
    std::cout << "shared_ptr<B> reference count = " << b.use_count() << "\n";

    std::cout << a1.GetB().GetCounter();

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
0

Note: I do not want to create copies of class B instance, I want A.getB().GetCounter() to always be the same as b.GetCounter().

Then you need to make A store a B& reference instead of a B object instance, eg:

class A {
public: 
    A(B& b);
    B& GetB();

private:
    B& b;
};
A::A(B& b) : b(b) {
}

B& A::GetB() {
    return b;
}

As long as the B object outlives the A object (which it does in your example), you will be fine, no (shared) pointers will be needed.

However, since you are declaring A before B, you can't use B at all in A as you have shown. The compiler won't know what B is while parsing A.

Since B doesn't depend on A for anything, you can simply swap the order of their declarations, eg:

class B {
public:
    B();
    void IncrementCounter();
    int GetCounter();

private:
   int counter = 0;
};

class A {
public: 
    A(B& b);
    B& GetB();

private:
    B& b;
};

Otherwise, if that is not an option for your situation, then you will have to use a forward declaration of B before declaring A, eg:

class B; // <--

class A {
public: 
    A(B& b);
    B& GetB();

private:
    B& b;
};

class B {
public:
    B();
    void IncrementCounter();
    int GetCounter();

private:
   int counter = 0;
};

Forward declaration only work when dealing with references and pointers, not with instances.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Side note: `A` containing a reference to `B` makes it tricky to copy `A` by assignment because the `B` member cannot be changed to refer to a different `B`. – user4581301 Feb 03 '23 at 19:42
  • @user4581301 True, but the copied `A` can refer to the same `B` instance as the original `A` does. But, if you need to change which `B` object an `A` instance refers to, you have to use a pointer instead of a reference. – Remy Lebeau Feb 03 '23 at 22:09