正如“更有效的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;
}
答案 0 :(得分: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)
是失败的操作(它可以是一个子派生对象,但对于赋值来说这应该是逻辑上可以的<强>来源)。
举个例子:
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;
};