C++ Implicit Copy Constructor For a Class That Contains Other Objects

C++ implicit copy constructor for a class that contains other objects

Foo f1;
Foo f2(f1);

Yes this will do what you expect it to:

The f2 copy constructor Foo::Foo(Foo const&) is called.

This copy constructs its base class and then each member (recursively)

If you define a class like this:

class X: public Y
{
private:
int m_a;
char* m_b;
Z m_c;
};

The following methods will be defined by your compiler.

  • Constructor (default) (2 versions)
  • Constructor (Copy)
  • Destructor (default)
  • Assignment operator

Constructor: Default:

There are actually two default constructors.

One is used for zero-initialization while the other is used for value-initialization. The used depends on whether you use () during initialization or not.

// Zero-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Zero-Initialization version'
,m_a(0) // Default construction of basic PODS zeros them
,m_b(0) //
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Zero-Initialization version'
{
}

// Value-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Value-Initialization version'
//,m_a() // Default construction of basic PODS does nothing
//,m_b() // The values are un-initialized.
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Value-Initialization version'
{
}

Notes: If the base class or any members do not have a valid visible default constructor then the default constructor can not be generated. This is not an error unless your code tries to use the default constructor (then only a compile time error).

Constructor (Copy)

X::X(X const& copy)
:Y(copy) // Calls the base copy constructor
,m_a(copy.m_a) // Calls each members copy constructor
,m_b(copy.m_b)
,m_c(copy.m_c)
{}

Notes: If the base class or any members do not have a valid visible copy constructor then the copy constructor can not be generated. This is not an error unless your code tries to use the copy constructor (then only a compile time error).

Assignment Operator

X& operator=(X const& copy)
{
Y::operator=(copy); // Calls the base assignment operator
m_a = copy.m_a; // Calls each members assignment operator
m_b = copy.m_b;
m_c = copy.m_c;

return *this;
}

Notes: If the base class or any members do not have a valid viable assignment operator then the assignment operator can not be generated. This is not an error unless your code tries to use the assignment operator (then only a compile time error).

Destructor

X::~X()
{
// First runs the destructor code
}
// This is psudo code.
// But the equiv of this code happens in every destructor
m_c.~Z(); // Calls the destructor for each member
// m_b // PODs and pointers destructors do nothing
// m_a
~Y(); // Call the base class destructor
  • If any constructor (including copy) is declared then the default constructor is not implemented by the compiler.
  • If the copy constructor is declared then the compiler will not generate one.
  • If the assignment operator is declared then the compiler will not generate one.
  • If a destructor is declared the compiler will not generate one.

Looking at your code the following copy constructors are generated:

Foo::Foo(Foo const& copy)
:bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
:i(copy.i)
,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
:j(copy.j)
{}

C++ behavior of a default(implicit) copy constructor in a derived class

When you have a derived class copy constructor such as...

C(const C& c) {...}

You might think this would call A's and B's copy ctors automatically, but it doesn't. The implicit behavior is as if you had written...

C(const C& c) : B() {...}

... And then B's B() does...

B() : A() {...}

If you want copy ctors to be called up to your base classes you need to explicitly specify that behavior like so...

C(const C& c) : B(c) {...}

Implicitly generated copy ctors already do this for you.

As far as your observation that operator= is called in your situation, it isn't. I don't know why you think it is.

C++ Copy Constructors: must I spell out all member variables in the initializer list?

C++ Copy Constructors: must I spell out all member variables in the initializer list?

Yes, if you write a user defined copy constructor, then you must write an initialiser for every sub object - unless you wish to default initialise them, in which case you don't need any initialiser - or if you can use a default member initialiser.

the object contains some member variables which are pointers to its other member variables)

This is a design that should be avoided when possible. Not only does this force you to define custom copy and move assignment operators and constructors, but it is often unnecessarily inefficient.

But, in case that is necessary for some reason - or custom special member functions are needed for any other reason - you can achieve clean code by combining the normally copying parts into a separate dummy class. That way the the user defined constructor has only one sub object to initialise.

Like this:

struct just_data {
// many members... details don't matter
R just_data_1;
S just_data_2;
T just_data_3;
U just_data_4;
V just_data_5;
};

struct fancy_copying : just_data
{
fancy_copying(const fancy_copying& other)
: just_data(other.just_data)
{
do_the_fancy();
}
};

Dereferencing syntax in copy constructor for class that contains other objects

No, that doesn't really make sense. What makes sense is this:

MyClass::MyClass(const MyClass &obj) {
var = new OtherClass(*obj.var);
}

Or rather:

MyClass::MyClass(const MyClass &obj)
:var(new OtherClass(*obj.var))
{}

Can the compiler generates a default copy constructor that takes reference to different class type?

Can the compiler generates a default copy constructor that takes reference to different class type?

By definition, no. A constructor that accepts an object of another type is not a copy constructor. It would be a converting constructor.

No such converting constructor is implicitly generated.

This is an aggregate initialization, but I can't understand how d is constructed?

No constructor of the enclosing class is called in aggregate initialisation. The sub objects are initialised directly.

D is an aggregate with a base class of type B. Aggregate initialisation is used to initialise this base sub object.

It's clear that the compiler does not generate D::D(const D&) because const D& = B() is ill-formed.

Former cannot be deduced from the latter. In fact, there is a (trivial) D::D(const D&) which you can prove by attempting copy initialisation:

D d1{};
D d2(d1); // works

That said, a trivial constructor is a concept for the abstract machine, and the compiler doesn't have to generate anything in practice.



Now what would happen if I inherits constructors from B

struct D : B { using B::B; };
D d{ B() }; // why it's ill-formed?

Having inherited constructors disqualifies the class from being an aggregate and hence aggregate initialisation does not apply. List initialisation will attempt to call a constructor, but no converting constructor exists.

Does the implicitly defined copy constructor in C++ call copy constructor for members too right?

Yes, that is exactly what it does.

calling default (implicit) copy constructor from the defined copy constructor

Actually the given code compiles without errors on Visual Studio 2013. However if the signature of the signal is changed to void completed(CustomData data); then a similar error occurs. Since you have not specified the compiler, but I assume it is gcc/clang you could get the error because template handling differs a bit. If so, the error is most likely caused by Q_DECLARE_METATYPE(CustomData) since it tries to generate the copy constructor inside. Change it to Q_DECLARE_METATYPE(CustomData*) (because that is what you really need, you are queuing arguments of type CustomData* not CustomData, at least in the given sample) and if you really have no signals that send the CustomData instances by value you should be fine.

Also, if you are passing the object by pointer then you should use Q_DECLARE_METATYPE(CustomData*). But I'd advise passing it through a std::unique_ptr or std::shared_ptr to prevent memory leaks.



Related Topics



Leave a reply



Submit