First off, I know the assignment operator cannot be defined in a class that has some subclasses. I understand it is because we don't want to make Subclass1 = Subclass2
possible.
But let's assume Class
is an abstract class and Subclass
is its... ya know. Then, is it feasible to do something like this?
Class* p = new Subclass;
Subclass s1;
*p = s1;
Actually, I tried implementing that in my code, but it didn't work :) Could you please help?
My full code:
#include <cstdlib>
#include <iostream>
#include <typeinfo>
using namespace std;
class BadIndex{
int index;
public:
BadIndex(int i):index(i){}
int getIndex(){ return index; }
};
template <typename t>
class Wielomian{
public:
~Wielomian(){}
virtual int getDeg() = 0;
virtual t& operator [](int) = 0;
virtual bool operator ==(Wielomian<t>&) = 0;
virtual Wielomian<t>& operator +(Wielomian<t>&) = 0;
virtual Wielomian<t>& operator +=(Wielomian<t>&) = 0;
};
template <typename t>
class TabWiel: public Wielomian<t>{
int deg;
t* plnml;
public:
TabWiel(t tab[] = {}, int size = 0);
~TabWiel();
TabWiel(const TabWiel<t>&);
TabWiel<t>& operator =(const TabWiel<t>&);
template <typename st>
friend ostream& operator <<(ostream& s, TabWiel<st>& tw);
int getDeg(){ return deg; }
t& operator [](int);
bool operator ==(Wielomian<t>&);
TabWiel<t>& operator +(Wielomian<t>&);
TabWiel<t>& operator +=(Wielomian<t>&);
};
template <typename t>
TabWiel<t>& TabWiel<t>::operator =(const TabWiel<t>& tw){
if (this != &tw){
delete[] plnml;
deg = tw.deg;
plnml = new t[deg + 1];
for (int i = 0; i < deg + 1; i++)
plnml[i] = tw.plnml[i];
}
return *this;
}
template <typename t>
TabWiel<t>::TabWiel(t tab[], int size){
deg = size - 1;
plnml = new t[deg + 1];
for (int i = 0; i < deg + 1; i++)
plnml[i] = tab[i];
if (deg == -1){
deg = 0;
plnml[0] = 0;
}
}
template <typename t>
TabWiel<t>::~TabWiel(){
delete[] plnml;
}
template <typename t>
TabWiel<t>::TabWiel(const TabWiel<t>& tw){
deg = tw.deg;
plnml = new t[deg + 1];
for (int i = 0; i < deg + 1; i++)
plnml[i] = tw.plnml[i];
}
template <typename t>
t& TabWiel<t>::operator [](int s){
if (s >= 0 && s < deg + 1)
return plnml[s];
else
throw BadIndex(s);
}
template <typename t>
bool TabWiel<t>::operator ==(Wielomian<t>& tw){
try{
TabWiel<t>& rhs = dynamic_cast<TabWiel<t>&>(tw);
if (deg == rhs.deg){
for (int i = 0; i < deg + 1; i++){
if (plnml[i] != rhs.plnml[i])
return false;
}
return true;
}
return false;
}
catch (const bad_cast& e){
cerr << "An exception" << e.what() << " thrown." << endl;
}
}
template <typename t>
ostream& operator <<(ostream& s, TabWiel<t>& tw){
for (int i = 0; i < tw.deg + 1; i++){
if (i != tw.deg)
s << tw.plnml[i] << "x^" << i << "+";
else
s << tw.plnml[i] << "x^" << i << endl;
}
return s;
}
template <typename t>
TabWiel<t>& TabWiel<t>::operator +(Wielomian<t>& tw){
try{
TabWiel<t>& rhs = dynamic_cast<TabWiel<t>&>(tw);
if (rhs.deg <= deg){
for (int i = 0; i < rhs.deg + 1; i++)
plnml[i] = plnml[i] + rhs.plnml[i];
return *this;
}
else{
t* tmp = new t[deg + 1];
for (int i = 0; i < deg + 1; i++)
tmp[i] = plnml[i];
int tmp_deg = deg;
delete[] plnml;
deg = rhs.deg;
plnml = new t[deg + 1];
for (int i = 0; i < deg + 1; i++){
if(i < tmp_deg + 1)
plnml[i] = tmp[i] + rhs.plnml[i];
else
plnml[i] = rhs.plnml[i];
}
return *this;
}
}
catch (const bad_cast& e){
cerr << "An exception" << e.what() << " thrown." << endl;
}
}
template <typename t>
TabWiel<t>& TabWiel<t>::operator +=(Wielomian<t>& tw){
try{
TabWiel<t>& rhs = dynamic_cast<TabWiel<t>&>(tw);
TabWiel<t>* nowy = new TabWiel<t>;
TabWiel<t> copy;
copy = *this;
*nowy = copy + rhs;
return *nowy;
}
catch (const bad_cast& e){
cerr << "An exception" << e.what() << " thrown." << endl;
}
}
I wish the assignment of *p
to non-empty subclass object worked. But it doesn't - all the code does, is that it enters "Wielomian" definition and then proceeds to the next line of the main
function (which in my case is the last line).
Your question is very interesting.
First of all, your code doesn't work because of slicing: you have two objects of Subclass
, but
the compiler thinks that one of it is only a Class
. So the code generated copies only the common
part of the data.
To demonstrate this, let's ellaborate on gha.st 's initial code extract:
struct Class { int a; virtual void hugo() = 0; };
struct Subclass : Class { int b; void hugo() override { cout<<"sub"<<a<<b<<endl; } };
int main() {
Class* p = new Subclass;
static_cast<Subclass*>(p)->a = 2;
static_cast<Subclass*>(p)->b = 3;
Subclass s1;
s1.a = 4; s1.b=5;
*p = s1; // slicing !!
p->hugo();
return 0;
}
What happens here ? Well, b
member isn't copied, although *p
is in reality a Subclass
!
But *p
is still a Subclass
, so we could use polymorphism to get this work. The trick is to use an virtual clone()
member to clone
an object (the object shall know its own type) into a target, if the target has the same type.
Then you could define operator=()
for Class
to use this clone()
. This makes it handy to use, but the drawback is that you'll no longer
be able to rely on default operator=
for any descendent of Class
if you want to avoid an endless recursion.
Here the proof of concept:
struct Class {
int a;
virtual void hugo() = 0;
virtual bool clone(Class*t) = 0;
Class& operator=(Class& o) {
if (!o.clone(this)) { // try to clone on subclass on a target of same subclass
// here,the source and target might differ. Only common members can be copied
a = o.a;
}
return *this;
}
};
struct Subclass : Class {
int a,b;
void hugo() override { cout<<"sub"<<a<<b<<endl; }
bool clone(Class*t) {
cout<<"Clone ";
if (dynamic_cast<Subclass*>(t)) { // if source and target of same subclass
//*dynamic_cast<Subclass*>(t) = *this; // this doesn't work cause default operator will try to copy the Class base, causing recursion
dynamic_cast<Subclass*>(t)->a = a; // copy members
dynamic_cast<Subclass*>(t)->b = b;
return true;
}
else return false; // or tell that class of source and target are different.
}
};
Then you can use the main() function above, and see that the object is properly copied.
This trick is a kind of simplified double dispatch. You could even elaborate further by foreseing various kind of conversions depending on source and target subtype.