algolejos
algolejos

Reputation: 139

Initialize a const class member with a default value

I have two classes A and B. bclass of type B is a constant member of class A; what I want to do is to initialize class bclass with default values if no B object is provided to A. Something like this:

#include <iostream>
#include <string>
#include <unistd.h>

using namespace std;

class B{
public:
  B(string Bs): Bstring(Bs){
    cout << "B constructor: " << Bstring << endl;
  }

  ~B(){
    cout << "B destructor: " << Bstring << endl;
  }

private:
  const string Bstring;
};

class A{
public:
  A(const B subb = B("mmmmm")): bclass(subb){
    cout << "A constructor." << endl;
  }

  ~A(){
    cout << "A destructor." << endl;
  }

private:
  const B bclass;
};

int main(void){
    A a;
    cout << "doing work..." << endl;
    sleep(2);
    return 0;
}

The output is:

B constructor: mmmmm
A constructor.
B destructor: mmmmm
doing work...
A destructor.
B destructor: mmmmm

The thing is that I'm constructing 2 B classes(?) when only one is needed! And somehow, B constructor is called only once, while the destructor is called twice... What is going on?!

EDIT 1:

After reading the (great) response of @WhiZTiM, I added the next two updates...

The next code explains when the second constructor is called:

#include <iostream>
#include <string>
#include <unistd.h>

using namespace std;

class B{
public:
  B(string Bs): Bstring(Bs){
    cout << "B constructor: " << Bstring << endl;
  }
  B(const B& bobj): Bstring(bobj.Bstring + "(copy)"){
    cout << "B copy constructor: " << Bstring << endl;
  }

  ~B(){
    cout << "B destructor: " << Bstring << endl;
  }

private:
  const string Bstring;
};

class A{
public:
  A(const B& subb = B("mmmmm")): bobj(subb){
    cout << "A constructor." << endl;
  }

  ~A(){
    cout << "A destructor." << endl;
  }

private:
  const B bobj;
};

int main(void){
  A a;
  cout << "doing work..." << endl;
  sleep(2);
  return 0;
}

output:

B constructor: mmmmm
B copy constructor: mmmmm(copy)
A constructor.
B destructor: mmmmm
doing work...
A destructor.
B destructor: mmmmm(copy)

As @WhiZTiM pointed to me, the compiler elided a third call to the B constructor (Thanks!).

EDIT 2: As I want only 1 B object, the best idea is to use pointers. The code must be:

#include <iostream>
#include <string>
#include <unistd.h>

using namespace std;

class B{
public:
  B(const string Bs): Bstring(Bs){
    cout << "B constructor: " << Bstring << endl;
  }
  B(const B& bobj): Bstring(bobj.Bstring){
    cout << "copying an existing B object." << endl;
  }

  ~B(){
    cout << "B destructor: " << Bstring << endl;
  }

private:
  const string Bstring;
};

class A{
public:
  A(B* subb = new B("mmmmm")): objb(subb){
    cout << "A constructor." << endl;
  }

  ~A(){
    cout << "A destructor." << endl;
    delete objb;
  }

private:
  const B* const objb;
};

int main(void){
  A a1; // This will call the default B constructor.
  A a2(new B("ooooo"));// This is calling a non default B object constructor.
  cout << "doing work..." << endl;
  sleep(2); //I need more motivation...
  return 0;
}

This is the output:

B constructor: mmmmm
A constructor.
B constructor: ooooo
A constructor.
doing work...
A destructor.
B destructor: ooooo
A destructor.
B destructor: mmmmm

Thanks a lot to @WhiZTiM

Upvotes: 1

Views: 346

Answers (1)

WhiZTiM
WhiZTiM

Reputation: 21576

The thing is that I'm constructing 2 B classes(?) when only one is needed! And somehow, B constructor is called only once, while the destructor is called twice... What is going on?!

There is no magic going on. The compiler generated implicit copy and move constructors. To get the true picture, implement yours


class A{
public:
  A(const B subb = B("mmmmm")): bclass(subb){
    cout << "A constructor." << endl;
  }

  ~A(){
    cout << "A destructor." << endl;
  }

private:
  const B bclass;
};

In your constructor, B is created using your single argument constructor, then assigned to subb, it is then copied to bclass... So there were actually 3 instances of B created.

  • The first is your User-defined constructor, which is why you see your output
  • The second is a move constructor used to construct subb - compiler generated default
  • The third is a copy constructor used to construct bclass - compiler generated default

You may have seen 2 because, the compiler elided a call

Upvotes: 1

Related Questions