In the following code, the function foo is copy constructing a Base object c from a Derived object d. My question is: are we getting an exact copy? Because I'm not getting the polymorphic behavior I'm expecting
#include<iostream>
class Base
{
public:
virtual void sayHello()
{
std::cout << "Hello Base" << std::endl ;
}
};
class Derived: public Base
{
public:
void sayHello() override
{
std::cout << "Hello Derived" << std::endl ;
}
};
void foo(Base* d)
{
Base* c = new Base(*d);
c->sayHello() ;
}
int main()
{
Derived d;
foo(&d) ; //outputs Hello Base
}
There is no virtual constructor nor copy constructor.
However, it is possible to define a function that behaves like one.
In my case, it is the virtual member function copy() which I added to OP's sample:
#include <iostream>
class Base
{
public:
virtual Base* copy() const { return new Base(*this); }
virtual void sayHello()
{
std::cout << "Hello Base" << std::endl ;
}
};
class Derived: public Base
{
public:
virtual Base* copy() const override { return new Derived(*this); }
void sayHello() override
{
std::cout << "Hello Derived" << std::endl ;
}
};
void foo(Base* d)
{
Base* c = d->copy();
c->sayHello() ;
}
int main()
{
Derived d;
foo(&d) ; //outputs Hello Derived
return 0;
}
Output:
Hello Derived
Live Demo on coliru
The drawback is that every derived class of Base has to provide it to make it function properly. (I've no idea how to convince the compiler to check this for me with any trick.)
A partial solution could be to make copy() pure virtual in the class Base (assuming it is not meant to be instantiable).
you may wonna change the line of new
Base* c = new Derived(*d);
so you have the type Derived in a Base pointer. During runtime it is looked up, which type it is and you get the right output.
let me know if im wrong... just created this out of my mind on the fly.
To answer your question about whether or not this is copy constructing lets add some members. Base will have a member, m_b and Derived will inherit m_b but also have another member m_d
#include <iostream>
struct Base {
const int m_b;
Base() = delete;
Base(const int a_b) : m_b(a_b) {}
virtual void sayHello() {
std::cout << "Base " << m_b << std::endl;
}
};
struct Derived : public Base {
const int m_d;
Derived() = delete;
Derived(const int a_b, const int a_d) : Base(a_b), m_d(a_d) {}
void sayHello() override {
std::cout << "Derived " << m_b << ' ' << m_d << std::endl;
}
};
void foo(Derived* a) {
Base* b = new Base(*a);
b->sayHello(); // Output is "Base 1", 1 was copied from argument a
}
void bar(Derived* a) {
Base* d = new Derived(*a);
d->sayHello(); // Output is "Derived 1 2"
}
int main() {
Derived d(1, 2);
foo(&d);
bar(&d);
return 0;
}
The line:
Base* b = new Base(*a);
Created a Base and so sayHello calls Base's implementation which doesn't know about m_d. However this line does copy m_b from the derived class
The line:
Base* d = new Derived(*a);
Created a Derived and so sayHello calls Derived's implementation which copied both m_b and m_d
Expected polymorphic behavior will come into existence when the Base class pointer points to Derived class object. Then at run time the actual type of object pointed to by the Base class pointer will be checked and appropriate function will get called.
Base* c = new Base(*d); // <<-- case of object slicing
Here, c points to Base class object. Therefore, c->sayHello() ; is bound to call the Base::sayHello() at runtime.
are we getting an exact copy?. No since you are creating a Base object due to new Base. Due to object slicing the Base part of the *d object is passed to copy c'tor and what you get is corresponding Base object.
Base *c = new Derived(*d); will give the expected behavior.
Related
This question already has answers here:
What is object slicing?
(18 answers)
Closed 3 years ago.
I have two classes, one derived from the other. I want a function that returns an object (not pointer or reference) of the base class that calls the derived virtual function.
#include <iostream>
using namespace std;
class Base
{
public:
virtual void print() { cout << "Base" << endl; }
};
class Derived : public Base
{
public:
void print() { cout << "Derived" << endl; }
};
Base go()
{
Derived *derived = new Derived();
Base *base = derived;
base->print();
// (*base).print(); // prints "Derived" !
// Base base2 = (*base);
// base2.print(); // prints "Base" !
return *base;
}
int main()
{
Base base = go();
base.print();
return 0;
}
This prints out
Derived
Base
So in the go() function I managed to convert to Base and the print works. But when I return the object the print is using the base function!
I know this can work if you return a pointer or a reference but I really need to return an object. Is this possible? Why does my code not work?
As you can see, I've commented out code in go() that de-references the upcast pointer. Strangely, it prints correctly! And if I do a conversion to an object, it doesn't!
Any insight into why this is all happening would be very much appreciated.
When you return an actual object (and not a refernce or a pointer) it calls its copy ctor. In this case you return a Base class, so it calls its copy constructor and creates a new Base class object.
#include <iostream>
using namespace std;
class Base
{
public:
Base() = default;
Base(const Base & base) { std::cout << "COPY BASE" << std::endl; }
virtual void print() { cout << "Base" << endl; }
};
class Derived : public Base
{
public:
Derived() = default;
Derived(const Derived & derived) { std::cout << "COPY DERIVED" << std::endl; }
void print() { cout << "Derived" << endl; }
};
Base go()
{
Derived *derived = new Derived();
Base *base = derived;
base->print();
// (*base).print(); // prints "Derived" !
// Base base2 = (*base);
// base2.print(); // prints "Base" !
return *base;
}
int main()
{
Base base = go();
base.print();
return 0;
}
In this case the output will be:
Derived,
COPY BASE,
Base
By returning the base object itself, you're dropping any reference to it having started off as derived and subsequently upcasted (and copied).
So effectively, what you're after will not work.
If you really "need this to return an object", I recommend a thin wrapper
struct karls_return_object {
// ... stuff ...
Base *b;
};
I have following code snippet:
class base
{
public:
virtual void print(char a){ std::cout << " Base\n"; }
};
class derived : public base
{
public:
void print(float a) { std::cout << " Derived\n"; }
};
int main() {
base* d = new derived;
d->print(1.5);
}
Output is Base.
Why is the output coming from the base function and not from the derived one?
You have not overridden the function in the base class, you have overloaded it: The version in the derived class takes a float as an argument, and is quite a different beast from the base class method which takes a char. Moreover, the float version in the derived class shadows the base class version: The base class version becomes inaccessible for calls on the derived type.
As such, the following code
Derived d;
d.print('a');
d.print(1.5);
will print
Derived
Derived
because the call is resolved by the compiler to the only version of print() that's available in Derived.
Likewise, when you call d->print(1.5) through a pointer to Base, the derived class' version is inaccessible to the call: The compiler looks at the base class, sees that there is no print() method defined with a float argument, and converts the argument to char instead. It then calls the only implementation of print(char), which happens to be supplied by the base class.
If you simply change the signature of the print() method in the derived class to match that of the base class, the odd behavior will go away.
When you declare Base* d = new Derived;, the type of the class is Base, as printed by typeid(d).name(), so this instance doesn't have access to child class methods. If you change the type to Derived, you'll call the child method:
#include <iostream>
#include <typeinfo>
class Base
{
public:
virtual void print(char a) {
std::cout << " Base " << std::endl;
}
};
class Derived : public Base
{
public:
void print(float a) {
std::cout << " Derived " << std::endl;
}
};
int main()
{
Derived* d = new Derived;
std::cout << "class type is: " << typeid(d).name() << std::endl;
d->print(1.5);
return 0;
}
Output:
class type is: P7Derived
Derived
Furthermore, declaring the parent class print method virtual doesn't allow an instance of Base to call the child version of print because the child hasn't overridden it (different headers). Creating an instance of Base with Base *d = new Derived; and changing the Derived print method header to void print(char a) in the Derived class would allow you to call the child print method and output Derived, even from an instance of Base, using the virtual keyword.
#include <iostream>
#include <typeinfo>
class Base
{
public:
virtual void print(char a) {
std::cout << " Base " << std::endl;
}
};
class Derived : public Base
{
public:
void print(char a) {
std::cout << " Derived " << std::endl;
}
};
int main()
{
Base* d = new Derived;
std::cout << "class type is: " << typeid(d).name() << std::endl;
d->print(1.5);
return 0;
}
Output:
class type is: P4Base
Derived
I saw this example:
http://www.cplusplus.com/doc/tutorial/typecasting/#dynamic_cast
(...)
class Base { virtual void dummy() {} };
class Derived: public Base { int a; };
(...)
Base * pba = new Derived;
Base * pbb = new Base;
(...)
Why 'pba' is a Base object if it's being initialized with Derived? Why not make it a Derived object?
Derived * pba = new Derived; // use this instead
And is it just a C++ thing?
neither pba nor pbb is an object but they are pointers of type base class Base so in your code you used he pointers polymorphically which means a base pointer can point to the same class or to its derived class object.
the object is created with new not pbb or pba themselves, consider this example:
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
virtual void Print() const { cout << "Base print()" << endl;} // virtual function
void Greet()const {cout << "in Base Say: hello!" << endl;}
};
class Derived : public Base
{
public:
void Print() const { cout << "Derived print()" << endl;} // ovrode the base member pritn()
void Greet()const {cout << "in Derived Say: hello!" << endl;}
};
int main()
{
Base* pba = new Derived;
pba->Print(); // Derived print()
pba->Greet(); // in Base Say: hello! ??? because Greet() is not virtual
Base* pbb = new Base;
pbb->Print(); // Base print()
pbb->Greet(); // in Base Say: hello!
return 0;
}
so at runtime the pointer pba and pbb can be assigned an object of Base or Derived classes thus the virtual member function are called accordingly.
I have the following code:
main.hxx:
#include <iostream>
class Base{
public:
Base() {}
~Base() {}
virtual void whoAreYou() { std::cout << "I am base!" << std::endl;}
};
class Sub : public Base{
public:
Sub() {}
~Sub() {}
virtual void whoAreYou() { std::cout << "I am Sub!" << std::endl;}
};
class Factory {
public:
static Base getBase() { return Base(); }
static Base* getBasePtr() { return new Base(); }
static Base getSub() { return Sub(); }
static Base* getSubPtr() { return new Sub(); }
};
main.cxx
#include "main.hxx"
int main (int argc, char **argv) {
// Non pointers
Factory::getBase().whoAreYou();
Factory::getSub().whoAreYou();
// Pointers
Base* basePtr = Factory::getBasePtr();
Base* subPtr = Factory::getSubPtr();
basePtr->whoAreYou();
subPtr->whoAreYou();
delete basePtr, subPtr;
return 0;
}
When run, it prints the following:
I am base!
I am base!
I am base!
I am Sub!
I was expecting "Factory::getSub().whoAreYou();" to print "I am Sub!". Is it because when not using a pointer it gets casted to a Base?
I was expecting Factory::getSub().whoAreYou(); to print "I am Sub!".
No, the function returns a Base, so a Base is what you get.
Is it because when not using a pointer it gets casted to a Base?
Yes (although the word is "converted" not "casted" - a cast is an explicit conversion, and this conversion is implicit). This is sometimes called "slicing", since the derived-class part of the object is "sliced off" when it's copied.
Also, beware that the following:
delete basePtr, subPtr;
only deletes subPtr. You need a separate delete expression for each. You also need a virtual destructor in Base to safely delete subPtr.
This line creates a Sub then call Base default copy constructor to create a instance of Base from a the instance Sub:
static Base getSub() { return Sub(); }
Hence your log.
More generaly a Base is a Base instance, whereas Base* is a pointer on a Base instance or an object that inherits Base.
I have a simple code which doesn't work correctly with reference (polymorphism).
#include <iostream>
#include <string>
class Base {
public:
Base() {}
virtual ~Base() {}
virtual std::string text() const {
return "Base";
}
};
class Derived: public Base {
public:
Derived(Base& _b): b(_b) {}
virtual ~Derived() {}
virtual std::string text() const {
return b.text() + " - Derived";
}
private:
Base& b;
};
int main(int argc, char const *argv[])
{
Base b;
Derived d1(b);
std::cout << d1.text() << std::endl;
Derived d2(d1);
std::cout << d2.text() << std::endl;
return 0;
}
And output:
Base - Derived
Base - Derived
The second line in output I expected: Base - Derived - Derived. I read some resources and polymorphism work perfectly with reference and pointer but in this situation, it doesn't. If I replace reference by pointer, it work again. So, anybody can give me some explainations?
Thanks so much!
You're invoking the default copy constructor to Derived. Therefore when finished d2 will be a simple member-copy of d1, and both their b members will reference the same Base instance.
To prove this, add this to your Derived class
class Derived: public Base {
public:
Derived(Derived& d) : b(d) {}
Derived(Base& _b): b(_b) {}
virtual ~Derived() {}
virtual std::string text() const {
return b.text() + " - Derived";
}
private:
Base& b;
};
With this your output will become:
Base - Derived
Base - Derived - Derived
And just note, this is not a grand idea or a stellar learning example of polymorphism. (But it is an interesting example of construction overriding). Also note this is NOT a typical override of default copy-construction (where the parameter is a const-ref-type). Thus part of the reason this is not the greatest sample.
If you instrument the code you will see that when you call Derived d2(d1) the Derived::Derived(Base&)
constructor is not being called. This is because the d1 argument is a better match for the
implicit copy constructor, which just copies the b member from d1 to d2.
In order to see the behavior you expect, you can explicitly cast the d1 to (Base&)d1. If you do
so you will get code like the following (with the instrumentation):
#include <iostream>
#include <string>
class Base {
public:
Base() {}
virtual ~Base() {}
virtual std::string text() const {
return "Base";
}
};
class Derived: public Base {
public:
Derived(Base& _b): b(_b) {std::cout << "init'ed with: " << _b.text() << std::endl;}
virtual ~Derived() {}
virtual std::string text() const {
return b.text() + " - Derived";
}
private:
Base& b;
};
int main(int argc, char const *argv[])
{
std::cout << "Creating Base" << std::endl;
Base b;
std::cout << "Creating d1" << std::endl;
Derived d1(b);
std::cout << d1.text() << std::endl;
std::cout << "Creating d2" << std::endl;
Derived d2(d1);
std::cout << d2.text() << std::endl;
std::cout << "Creating d3" << std::endl;
Derived d3((Base&)d1);
std::cout << d3.text() << std::endl;
return 0;
}
And this gives the expected output:
Creating Base
Creating d1
init'ed with: Base
Base - Derived
Creating d2
Base - Derived
Creating d3
init'ed with: Base - Derived
Base - Derived - Derived
Your d1 and d2 both have type Derived so this is working correctly. Typically the references are reversed; e.g.
Base b;
Derived d;
Base &dr = d;
std::cout << b.text() << std::endl;
std::cout << dr.text() << std::endl;
Here text() is invoked through a Base type but the latter will call the version in Derived.
Note that it doesn't typically make sense to allow a derived class to be initialized via a base class. Suppose you add type Derived2 that has abilities or state much different from Derived. This constructor would allow
Derived2 d2;
Derived d1(d2);
which is likely a very bad idea.
As noted correctly in the comment, it is now using the default copy constructor, and that is the reason for your observation with the same output for both. So, d1 is just copied into d2 rather than used for the base member variable inside d2.