4

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).

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Jules
  • 247
  • 2
  • 8

1 Answers1

2

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.

Christophe
  • 68,716
  • 7
  • 72
  • 138