2013-11-30 12 views
2

Как говорили в пункте 33 в «более эффективные C++», проблема присваиванияКак абстрактный базовый класс избегает частичного присвоения?

//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; 
} 
+0

Так животное является конкретным или абстрактным? и вы дважды написали «pa» (в вашем коде нет «pb»). – flyman

+0

@flyman Извините, я только что исправил свои ошибки и дал понять, что понял. – particle128

+0

Попробуйте с чистой виртуальной функцией, которая не является деструктором. – johnbakers

ответ

1

Это не важно, что ваш базовый класс имеет чисто виртуальные функции, потому что вы не определили operator= для любого из классов. Поэтому, когда компилятор видит это заявление:

*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; 
} 
+0

Если вы явно определяете operator = в каждом классе в этой иерархии деривации, абстрактный базовый класс не нужен. – particle128

2

Абстрактный базовый класс не может помочь в случае уступки, так как базовый суб-объект не обрабатывается (то, что абстрактный класс будет блокировать), но отсекает производный объект (т. е. назначение выполняется между уже существующими базовыми под-объектами).

Чтобы избежать этой проблемы единственное решение, я могу думать, чтобы это

  1. сделать назначение виртуального
  2. проверки в задании, что экземпляр источника правильного типа

В коде

#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) является операцией n, который потерпит неудачу, если объект, переданный оператору присваивания, не имеет правильного производного типа (он может быть суб-производным объектом, но это должно быть логически нормально для назначения источник).

В качестве примера:

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

«базовый под-объект не создается (что бы абстрактный блок блокировал), но отсечен от производного объекта (т. Е. Назначение выполняется между уже существующими базовыми под-объектами)». Хорошая точка зрения. – particle128

0

Поскольку вы не обрабатываются операторы присваивания в классах, вы приземлитесь в ситуации частичной уступке, как шотландец четко описывает в своей статье.

Вам необходимо обрабатывать назначения в своих классах.В текущем проекте по умолчанию вызывается неявный оператор присваивания Ab, и, следовательно, все свойства класса children теряются.

Чтобы избежать этого, вы должны иметь реализацию так:

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

Если вы явно определяете operator = в каждом классе в этой иерархии деривации, абстрактный базовый класс не нужен. – particle128

+0

@ particle128 operator = в каждом классе должен обрабатываться случай, когда объект дочерних классов приравнивается, и мы хотим избежать уравнения разных дочерних классов. Также имея оператор = в каждом дочернем классе, вы делаете возможным полное назначение. И нужно стремиться к абстрактному базовому классу, который просто определяет специализацию понятия и дочерних классов. – Nik

Смежные вопросы