rookie
rookie

Reputation: 7843

strange behavior of initialization in C++

I have two classes Base and Derived from it:

class Base{
public:
    Base(int = 0);
    Base(Base&);
    Base& operator=(const Base&);
protected:
    int protectedData;
private:
    int baseData;
};

/////////////DERIVED CLASS
class Derived: public Base{
public:
    Derived(int = 0);
    Derived(Derived&);
    Derived(Base&);
    Derived& operator=(const Derived&);
private:
    int derivedData;
};

implementation of the functions

///////////BASE FUNCTIONS
Base::Base(int value): protectedData(value), baseData(value)
{
    cout << "base C'tor" << endl;
}


Base::Base(Base& base)
{
    baseData = base.baseData;
    protectedData = base.protectedData;
    cout << "base Copy C'tor" << endl;
}

Base& Base::operator=(const Base& base)
{
    if(this == &base) return *this;
    baseData = base.baseData;
    protectedData = base.protectedData;
    cout << "Base::operator=" << endl;
    return *this;
}

///////////DERIVED FUNCTIONS

Derived::Derived(int value): Base(value), derivedData(value)
{
    cout << "derived C'tor" << endl;
}

Derived::Derived(Derived& derived)
    : Base(derived)
{
    derivedData = derived.derivedData;
    cout << "derived Copy C'tor" << endl;
}

Derived::Derived(Base& base)
    : Base(base), derivedData(0)
{
    cout << " Derived(Base&) is called " << endl;
}

Derived& Derived::operator=(const Derived& derived)
{
    if(this == &derived) return *this;

    derivedData = derived.derivedData;
    cout << "Derived::operator=" << endl;
    return *this;
}

With the following in my main:

Base base(1);
Derived derived1 = base;

the compiler gives me an error:

..\main.cpp:16: error: no matching function for call to `Derived::Derived(Derived)'
..\base.h:34: note: candidates are: Derived::Derived(Base&)
..\base.h:33: note:                 Derived::Derived(Derived&)
..\base.h:32: note:                 Derived::Derived(int)
..\main.cpp:16: error:   initializing temporary from result of `Derived::Derived(Base&)'

but when I have this in main:

Base base(1);
Derived derived1(base);

it works perfectly. Why?

EDITED

so ok thanks for everybody, I checked it with const and all works good, BUT I check also all calls and in both cases I receive:

base C'tor
base Copy C'tor
Derived(Base&)

my question is, why? You said that I actually call: Derived(Derived(Base&)) so I must have

base C'tor
base Copy C'tor
Derived(Base&)
Derived copy c'tor //<-note, why this one is missing?

Upvotes: 3

Views: 356

Answers (5)

SCFrench
SCFrench

Reputation: 8374

Change these constructors

Base(Base&);
Derived(Derived&);
Derived(Base&);

To take const references:

Base(const Base&);
Derived(const Derived&);
Derived(const Base&);

The former cannot accept temporary values, the latter can. The compiler wants to convert

Derived derived1 = base;

into

Derived derived1(Derived(base));

but it can't because Derived(base) is a temporary value and there is no Derived constructor that can take a temporary Derived instance.

Edit:

Note that it is sometimes difficult to see what the compiler is actually doing by putting a bunch of cout calls in the constructors, because of copy elision. Copy elision allows the compiler in certain circumstances to eliminate copies, even if those copies have side effects (like printing output). There's a reasonably good discussion of this in Wikipedia. If you are using g++, you can add the --no-elide-constructors switch and you will see all the expected copies take place.

Also, this answer by litb to another related question has a lot of detailed discussion of the subtle differences between direct initialization and copy initialization. It's good reading!

Upvotes: 6

rookie
rookie

Reputation: 7843

I think I understood, it is just optimization of my compiler, there is no need create copy of the derived object cause it is alreary created

Upvotes: 0

usta
usta

Reputation: 6869

Try making copy constructors accept by const reference (as they should), and then it'll work. Reason:

Derived derived1 = base;

creates an rvalue (temporary object) of Derived from base using Derived::Derived(Base&), which then can't be passed to Derived::Derived(Derived&) because an rvalue can't be bound to a non-const reference.

Upvotes: 4

TinkerTank
TinkerTank

Reputation: 5805

In the faulty code, you are performing an implicit cast: Derived derived1 = base; forces the compiled to cast the 'base' object of class 'Base' to be casted to derived.

However, It's never possible to cast a base class to a derived class, because the derived class might have additional data that doesn't exist in the base class. The other way round, things should work fine. It's important to realize that a cast is used here, and not the constructor.

The working version of the code doesn't have the problem. It's not performing a cast, it's just calling the Derived(Base&); constructor which has been defined in your code.

Upvotes: 0

Mario The Spoon
Mario The Spoon

Reputation: 4859

The solution will most likely be adding an assignmend operator to derived:

Derived& Derived::operator=(const Base& base);

Otherwise the compiler will try to build temporary instances of classes - which he informed you about!

Also, copy constructors are MyClass( const MyClass& instance), they should take const references as arguments.

hth

Mario

Upvotes: 1

Related Questions