抽象基类如何避免部分赋值
How does the abstract base class avoid the partial assignment?
正如"更有效的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;
}
在赋值的情况下,抽象基类没有帮助,因为基子对象没有实例化(抽象类会阻止什么),而是从派生对象中切片(即,在现有的基子对象之间进行赋值)。
为了避免这个问题,我唯一能想到的解决方案就是
- 使分配虚拟化
- 请在分配中检查源实例的类型是否正确
代码内
#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 farn";
*pa1 = *pb1; // Runtime error here (bad cast)
return 0;
}
基类具有纯虚拟函数并不重要,因为您还没有为任何类定义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;
}
由于您没有处理类中的赋值运算符,您遇到了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;
};
相关文章:
- 为"adjacent"变量赋值时出现问题
- C++中的赋值发生,尽管右侧出现异常
- 用C++中的sscanf赋值
- 为std::string的某个索引赋值
- 重载Singly Linked List中的赋值运算符
- 为什么我必须在C++中添加一个赋值符号来声明一个数组
- gtest_使用setargpointee在函数中赋值
- 非常量变量只读位置的赋值
- 使用赋值运算符重载从类中返回jobject
- C++数据文件、数组和计算赋值
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- 全局作用域中函数指针的赋值
- 避免在 ctor 初始值设定项列表中的字符串赋值中使用双重调用函数
- 如何避免重载赋值运算符将右值转换为左值
- 抽象基类如何避免部分赋值
- 避免在 c++ 构造函数主体中赋值的任何方法
- 如何避免拷贝赋值操作符的双重自由或损坏(fasttop)
- 避免派生类的赋值运算符中的重复
- 此变量赋值有何不同?
- 如何避免这段代码中的void赋值