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?

5
  • Reference or smart pointers are the way to go, if you don't want to have copies. Commented Feb 3, 2023 at 18:12
  • Constructor initialization list? A :: A(B new_b) : b(new_b) { ; } Commented Feb 3, 2023 at 18:12
  • @πάνταῥεῖ I dont know which one is better in my case in terms of efficiency Commented Feb 3, 2023 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. Commented Feb 3, 2023 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? Commented Feb 3, 2023 at 18:24

2 Answers 2

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;
}
Sign up to request clarification or add additional context in comments.

Comments

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.

2 Comments

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.