Search code examples
c++constructorargumentspass-by-referencelvalue

C++ initializing an object that contains another object design issue


I am attempting to write two classes A and B where B contains an instance of class A. In practice, A contains some large data. In this toy example, let's assume they are as follow:

class A{
public:
    int a;
    A(){}
    A(const A& ac){
       cout <<"copy constructor for A" << endl;
       a = ac.a;
    }
    virtual ~A(){}
    A& operator=(const A& that){
      cout <<"operator = for A" << endl;
      a = that.a;
      return *this;
   }
};
class B{
private:
    A ba;
public:
    B(){  a = A();  }
    B(A& a){
        cout << "Using default constructor" << endl;
        ba = a;
    }
    B(const B& copy){
        cout<<"copy"<< endl; ba = copy.ba;
    }
    B& operator=( const B& that){
       cout<<"invoking operator = "<< k<< endl;
       ba = that.ba;
       return *this;
    }
    virtual ~B(){}
    A& getA(){ return ba; }
    void printAadrr(){
        cout << &ba <<" "<< ba.a<< endl;
    }
};

The problem with this is that when initializing B using B::B(A& a), the argument is copied into the instance ba in B. It might be inefficient if a is large.

I thought of another approach where instead of

class B{
private:
    A ba;
    ....

I could do

class B{
    private:
        A* ba;
public:
    B(){  
       ba = new A(); //but we have to free memory in destructor if we call default constructor
    }
    B(A& a){
        cout << "Using default constructor" << endl;
        ba = &a;
    }

But this design choice has its own issue where if I make B by default constructor, I need to free the memory allocated for ba later on in the destructor.

Is there a convenient way to pass the "reference" of the object A (created outside of B) into B in the constructor if we use B::B(A& a) and create an empty, default A object if we use B::B()? I tried

B::B(A& a){
    &ba = &a;
}

but it gives "lvalue has to be on the left of assignment" error.

Any advice is appreciated. Sorry for the overly long post.


Solution

  • Define a constructor using move semantics as follows:

    B::B( A && a )
    : ba( std::move( a ) )
    {}
    

    Now you can transfer an instance into the object.

    A a;
    B b( std::move( a ) );
    // `b` stores content of `a`, and local `a` is no longer valid.
    

    You can still construct with the copy constructor if you want.

    A a;
    B b( a );
    // `b` stores a copy of `a`, and local `a` is still valid.