抽象基类如何避免部分赋值?

时间:2013-11-30 08:23:31

标签: c++

正如“更有效的C ++”第33项所述,分配问题是

//Animal is a concrete class
Lizard:public Animal{};
Chicken:public Animal{};

Animal* pa=new Lizard('a');
Animal* pb=new Lizard('b');
*pa=*pb;//partial assignment

但是,如果我将Animal定义为抽象基类,我们也可以编译并运行句子:*pa=*pb。部分分配问题仍然存在 看我的例子:

#include <iostream> 
class Ab{ private: int a;
        double b;
    public:
        virtual ~Ab()=0;
};
Ab::~Ab(){}

class C:public Ab{
    private:
        int a;
        double b;
};

class D:public Ab{
    private:
        int a;
        double b;
};

int main()
{
    Ab *pc=new C();
    Ab *pd=new D();
    *pc=*pd;
    return 0;
}

我错过了什么吗?那么抽象基类的真正含义是什么?

我自己得到了答案。我错过了书中的代码段。
在基类中使用受保护的operator=以避免*pa=*pb。使用抽象基类来避免animal1=animal2。然后唯一允许的表达式是lizard1=lizard2;chicken1=chicken2;
请参阅以下代码:

#include <iostream> 
class Ab{ 
    private: 
        int a;
        double b;
    public:
        virtual ~Ab()=0;
    protected:   //!!!!This is the point
        Ab& operator=(const Ab&){...}
};
Ab::~Ab(){}

class C:public Ab{
    public: 
        C& operator=(const C&){...}
    private:
        int a;
        double b;
};

class D:public Ab{
    public:
        D& operator=(const D&){...}
    private:
        int a;
        double b;
};

int main()
{
    Ab *pc=new C();
    Ab *pd=new D();
    *pc=*pd;
    return 0;
}

3 个答案:

答案 0 :(得分:2)

抽象基类在赋值的情况下无法帮助,因为基础子对象没有实例化(抽象类会阻塞什么),而是从派生对象中切除(即,在已经存在的基础子对象之间完成赋值) )。

为了避免这个问题,我能想到的唯一解决方案是

  1. 使分配虚拟
  2. 检查指定源实例的类型是否正确
  3. 代码

    #include <iostream>
    
    struct Base {
        int bx;
        Base(int bx) : bx(bx) {}
    
        virtual Base& operator=(const Base& other) {
            bx = other.bx;
            return *this;
        }
    };
    
    struct A : Base {
        int x;
        A(int bx, int x) : Base(bx), x(x) {}
        A& operator=(const Base& other) {
            const A& other_a = dynamic_cast<const A&>(other);
            Base::operator=(other);
            x = other_a.x;
            return *this;
        }
    };
    
    struct B : Base {
        int x;
        B(int bx, int x) : Base(bx), x(x) {}
        B& operator=(const Base& other) {
            const B& other_b = dynamic_cast<const B&>(other);
            Base::operator=(other);
            x = other_b.x;
            return *this;
        }
    };
    

    如果传递给赋值运算符的对象不是正确的派生类型,那么dynamic_cast<const A&>(other)是失败的操作(它可以是一个子派生对象,但对于赋值来说这应该是逻辑上可以的<强>来源)。

    举个例子:

    int main(int argc, const char *argv[]) {
    
        Base *pa1 = new A(1, 2);
        Base *pa2 = new A(3, 4);
        Base *pb1 = new B(5, 6);
        Base *pb2 = new B(7, 8);
    
        *pa1 = *pa2; std::cout << pa1->bx << "/" << dynamic_cast<A*>(pa1)->x << "\n";
        *pb1 = *pb2; std::cout << pb1->bx << "/" << dynamic_cast<B*>(pb1)->x << "\n";
        std::cout << "Ok so far\n";
    
        *pa1 = *pb1; // Runtime error here (bad cast)
    
        return 0;
    }
    

答案 1 :(得分:1)

您的基类具有纯虚函数并不重要,因为您尚未为任何类定义operator=。所以当编译器看到这个语句时:

*pc=*pd;

其中pc和pd都是Ab类型,它将调用Ab的默认赋值运算符,这将导致部分赋值。如下例所示,我得到的输出为"Abstract Base",它来自抽象基类:

class A {
public:
    virtual void foo() =0;
    virtual A& operator=(const A& rhs) {
        std::cout << "Abstract Base";
            return *this;
    }
};


class B : public A {
public:
    virtual void foo() {
        std::cout << "b:foo";
    }
};

class C : public A {
public:
    virtual void foo() {
        std::cout << "c:foo";
    }
};

int main()
{
    A* b = new B();
    A* c = new C();

    *b = *c;
    return 0;
}

答案 2 :(得分:0)

由于你没有处理你班级中的作业操作员,你可以按照Scot在他的文章中清楚描述的部分作业进行处理。

您需要处理班级中的作业。在当前设计中,调用Ab的默认隐式赋值运算符,因此子类的所有属性都将丢失。

为避免这种情况,你应该有这样的实现:

   class Ab{ private: int a;
        double b;
    public:
        virtual ~Ab()=0;
        virtual Ab& operator=(const Ab& rhs){cout<<"in Ab="<<endl;} 
};
Ab::~Ab(){}

class C:public Ab{

    C& operator=(const Ab& rhs){cout<<"in C="<<endl;
      return operator=(dynamic_cast<const C&>(rhs)); }

    C& operator=(const C& rhs){
       cout<<"do somethin in C="<<endl;
   }
    private:
        int a;
        double b;
};

class D:public Ab{

D& operator=(const Ab& rhs){cout<<"in D="<<endl;
      return operator=(dynamic_cast<const D&>(rhs)); }

    D& operator=(const D& rhs){
       cout<<"do somethin in D="<<endl;
   }
    private:
        int a;
        double b;
};