如果我 "slice" 到摘要 class 会发生什么

What happens if I "slice" to an abstract class

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

但是让我们假设 Class 是一个抽象 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 对非空子 class 对象的赋值工作有效。但事实并非如此——所有代码所做的,是它进入 "Wielomian" 定义,然后继续到 main 函数的下一行(在我的例子中是最后一行)。

你的问题很有意思。

首先,由于 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() 成员来克隆 如果目标具有相同的类型,则将对象(对象应知道自己的类型)转换为目标。
然后你可以为 Class 定义 operator=() 来使用这个 clone()。这使得它使用起来很方便,但缺点是你将不再 如果您想避免无休止的递归,可以为 Class 的任何后代依赖默认值 operator=

这里是概念证明:

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。您甚至可以通过根据源和目标子类型预测各种类型的转换来进一步详细说明。