以下内容为本人的烂笔头,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/iteWy7TyOQHEuxxi5gB6qA
多继承
回头看看前面的所有例子,你会发现接口都是单类继承,也就是一个派生类只基于一个基类派生。如果派生自多个基类,虚拷贝构造函数
和 虚构造函数
的实现有什么差异?
先来看看经典解决方案:
class BaseShape
{
public:
// ...
static BaseShape *Create(int id);
virtual BaseShape *Clone() = 0;
};
class BaseColor
{
public:
// ...
static BaseColor *Create(int id);
virtual BaseColor *Clone() = 0;
};
class Square : public BaseShape,
public BaseColor
{
public:
// ...
Square* Clone() {
return new Square(*this);
}
};
class Rectangle : public BaseShape,
public BaseColor
{
public:
// ...
Rectangle* Clone() {
return new Rectangle(*this);
}
};
BaseShape* BaseShape::Create(int id)
{
switch (id) {
case 1:
return new Square;
case 2:
return new Rectangle;
default:
std::cout << "unkown id" << endl;
return nullptr;
}
}
BaseColor* BaseColor::Create(int id)
{
switch (id) {
case 1:
return new Square;
case 2:
return new Rectangle;
default:
std::cout << "unkown id" << endl;
return nullptr;
}
}
同样基于协变(Covariance)的原因,虚拷贝构造函数
的派生类实现不依赖接口(也就是基类的定义)。从上面代码来看,这种接口返回裸指针的经典解决方案,虚拷贝构造函数
和 虚构造函数
的实现在多继承或者单继承时基本一致,也就是通用性非常好。
多继承的情况下,如果换做现代版本呢?
由于 虚构造函数
是在基类中创建静态成员函数 Create(int id),不需要在派生类中重写,所以就算多继承的情况下也没什么变化,但每个基类 T 都需要实现各自的静态成员函数 std::unique_ptr<T> Create(int id)
,如上面代码所示。
至于 虚拷贝构造函数
,基于前面的分析结论,协变(Covariance)不适用于其现代版本,也就是接口返回智能指针的做法在派生类的重写中会依赖基类接口定义,返回类型必须一样。
按照前面的思路,由于是多继承,假设再多一个基类 BaseColor,仍然在基类 T 中定义接口 Clone() 并在派生类中重写:
class BaseShape
{
public:
// ...
virtual std::unique_ptr<BaseShape> Clone() = 0;
};
class BaseColor
{
public:
// ...
virtual std::unique_ptr<BaseColor> Clone() = 0;
};
class Square : public BaseShape,
public BaseColor
{
public:
// ...
std::unique_ptr<BaseShape> Clone() {
return std::make_unique<Square>(*this);
}
std::unique_ptr<BaseColor> Clone() {
return std::make_unique<Square>(*this);
}
};
class Rectangle : public BaseShape,
public BaseColor
{
public:
// ...
std::unique_ptr<BaseShape> Clone() {
return std::make_unique<Rectangle>(*this);
}
std::unique_ptr<BaseColor> Clone() {
return std::make_unique<Rectangle>(*this);
}
};
上面这段代码是无法通过编译的,比如 std::unique_ptr<BaseShape> Square::Clone()
的返回类型与 std::unique_ptr<BaseColor> BaseColor::Clone()
的返回类型既不相同也不协变,也就是说无法绑定,所以编译器必然报错。
那么为了实现 虚拷贝构造函数
的现代化,应该怎么变通呢?
虚拷贝构造函数
在不同的基类中定义时取不同的接口名即可,如下
class BaseShape
{
public:
// ...
virtual std::unique_ptr<BaseShape> BaseShapeClone() = 0;
};
class BaseColor
{
public:
// ...
virtual std::unique_ptr<BaseColor> BaseColorClone() = 0;
};
class Square : public BaseShape,
public BaseColor
{
public:
// ...
std::unique_ptr<BaseShape> BaseShapeClone() {
return std::make_unique<Square>(*this);
}
std::unique_ptr<BaseColor> BaseColorClone() {
return std::make_unique<Square>(*this);
}
};
class Rectangle : public BaseShape,
public BaseColor
{
public:
// ...
std::unique_ptr<BaseShape> BaseShapeClone() {
return std::make_unique<Rectangle>(*this);
}
std::unique_ptr<BaseColor> BaseColorClone() {
return std::make_unique<Rectangle>(*this);
}
};
由于接口声明是在基类中,所以就算调用了不同的基类指针,仍然可以清楚应该调用哪个接口而不至于混肴。
class Demo
{
public:
Demo() {
int input;
cout << "Enter ID (1, 2): ";
cin >> input;
while ((input != 1) && (input != 2)) {
cout << "Enter ID (1, 2 only): ";
cin >> input;
}
// 通过 虚构造函数 创建对象
pShape = BaseShape::Create(input);
pColor = BaseColor::Create(input);
}
~Demo() {}
void Action() {
// 通过 虚拷贝构造函数 动态拷贝对象本身
auto pNewBase = pShape->BaseShapeClone();
auto pNewColor = pColor->BaseColorClone();
// do something
// pNewBase->xxx
// pNewColor->xxx
}
private:
std::unique_ptr<BaseShape> pShape;
std::unique_ptr<BaseColor> pColor;
};
本系列文章已完结。
好了,看完上面的内容,如果你觉得有意思或者对你有帮助,请拉到文章底部点个赞和在看,这是我的动力,谢谢!
关于 虚拷贝构造函数
和 虚构造函数
的概念就介绍到这里,相信你已经略懂一二,不信你来找我聊聊?往下拉,可扫码添加我为好友!