如果我“切片”到抽象类会发生什么

时间:2015-02-06 21:33:25

标签: c++ assignment-operator

首先,我知道无法在具有某些子类的类中定义赋值运算符。我理解这是因为我们不想让Subclass1 = Subclass2成为可能。

但我们假设Class是一个抽象类,Subclass是它的......你知道。那么,做这样的事情是否可行?

Class* p = new Subclass;
Subclass s1;
*p = s1;

实际上,我尝试在我的代码中实现它,但它没有用:) 你能帮忙吗?

我的完整代码:

#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;
    }
}

我希望将*p赋值给非空子类对象。但它没有 - 所有的代码都是,它进入“Wielomian”定义,然后进入main函数的下一行(在我的情况下是最后一行)。

1 个答案:

答案 0 :(得分:2)

你的问题非常有趣。

首先,由于slicing,您的代码无法正常工作:您有两个Subclass对象,但是 编译器认为其中一个只是Class。因此生成的代码只复制公共 部分数据。

为了证明这一点,让我们详细说明gha.st的初始代码提取:

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;
}  

这里发生了什么?好吧,b成员未被复制,但*p实际上是Subclass

但是*p仍然是Subclass,因此我们可以使用多态来实现这项工作。诀窍是使用虚拟clone()成员进行克隆 如果目标具有相同的类型,则对象(对象应知道自己的类型)进入目标 然后,您可以为operator=()定义Class以使用此clone()。这样可以方便使用,但缺点是你不再使用它 如果你想避免无休止的递归,能够依赖operator=的任何后代的默认Class

这里是概念证明:

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. 
    } 
};

然后你可以使用上面的main()函数,并看到对象被正确复制。

这个技巧是一种简化的double dispatch。您甚至可以通过根据源和目标子类型预测各种类型的转换来进一步详细说明。