1、常遇到的代码问题:
class Animal { //抽象类
public:
Animal& operator=(const Animal& rhs);
virtual ~Animal() = 0; //cpp文件必须提供实现
};
class Lizard: public Animal {
public:
Lizard& operator=(const Lizard& rhs); // 派生类赋值运算符函数中需要
// 显示调用基类赋值运算符函数。
};
class Chicken: public Animal {
public:
Chicken& operator=(const Chicken& rhs);
};
Lizard liz1;
Lizard liz2;
Animal *pAnimal1 = &liz1;
Animal *pAnimal2 = &liz2;
...
*pAnimal1 = *pAnimal2; // 错误,发生了部分赋值(基类部分)
原因:
1、因为编译器对于非virtual
函数(这里的赋值运算符函数)静态绑定,因此调用了pAnimal1
所属基类Animal
的赋值运算符函数,因此发生部分赋值。
注:函数参数永远是静态绑定。
2、解决方法:
将非尾端类设计为抽象类;将抽象类赋值运算符函数设为protected
。
class Animal { //抽象类
protected:
Animal& operator=(const Animal& rhs);
public:
virtual ~Animal() = 0; //cpp文件必须提供实现
};
class Lizard: public Animal {
public:
Lizard& operator=(const Lizard& rhs); // 派生类赋值运算符函数中需要
// 显示调用基类赋值运算符函数。
};
class Chicken: public Animal {
public:
Chicken& operator=(const Chicken& rhs);
};
Lizard liz1;
Lizard liz2;
liz2 = liz1; //正确,在派生类中,基类protected函数可见为public。
Animal *pAnimal1 = &liz1;
Animal *pAnimal2 = &liz2;
...
*pAnimal1 = *pAnimal2; // 编译错误,调用protected函数错误
解释:
1、同类型间的赋值被允许;
2、部分赋值被禁止(给出编译错误);
3、派生类的赋值操作函数可以调用基类的赋值操作函数。
3、其他解决方法:不好
将赋值运算符函数设置为virtual
,然后在函数内通过RTTI判断参数真实类型。
class Animal {
public:
virtual Animal& operator=(const Animal& rhs);
...
};
class Lizard: public Animal {
public:
virtual Lizard& operator=(const Animal& rhs);
...
};
class Chicken: public Animal {
public:
virtual Chicken& operator=(const Animal& rhs);
...
};
Lizard liz;
Chicken chick;
Animal *pAnimal1 = &liz;
Animal *pAnimal2 = &chick;
...
*pAnimal1 = *pAnimal2; // 调用Lizard类函数,函数内判断出参数类型不匹配,
// 给出不能赋值的提示。
解释:
1、因为使用虚函数(多态),所以可以正确调用所需类的成员函数;
2、因为使用虚函数,所以派生类中需要重写基类函数,所以赋值运算符函数参数都变为const Animal& rhs
,所以要在函数中判断参数真实类型。
函数中动态判断类型开销大,因此不适用。
总结:
1、在抽象类中,常把赋值运算符声明为:protected:
以便确保当目标静态类型是抽象类(基类)时,赋值操作被禁止(防止部分赋值)。
2、尽量将非尾端类设计为抽象类。