是变形还是被封装,这是一个问题!(c++)
To polymorph or to incapsulate, this is the question! (C++)
我需要将多态对象(假设Polygon
)存储在另一个对象(假设Simulation
)中。同时我想保持Simulation
的封装。
class Polygon {
public:
virtual double area() { return 0; }
};
class Square : public Polygon {
public:
Square(double edge) : edge_(edge) {}
virtual double area() { return edge_*edge_; }
private:
double edge_;
};
class Simulation {
public:
Simulation(Polygon& polygon) { polygon_ = &polygon; }
Polygon* polygon() { return polygon_; }
private:
Polygon* polygon_;
};
int main (int argc, const char * argv[]) {
Square square(2.0);
Simulation sim(square);
std::cout<<sim.polygon()->area()<<"n";
return 0;
}
这工作得很好!然而,它违反了Simulation
的封装,事实上,如果我从main
中去改变square
,它也会在Simulation
中改变。
我正在考虑使用复制构造函数修改Simulation
的构造函数:
Simulation(Polygon& polygon) { polygon_ = new Polygon(polygon); }
但是这将意味着我没有多态性…
显然我在这里错过了什么…干杯!
给Polygon添加一个克隆函数(和一个虚拟析构函数!)这是一个好主意,确保多边形是抽象的,所以确保至少有一个函数是纯虚的。
您的Simulation类将需要复制构造函数、析构函数和赋值操作符。注意,Square克隆函数可以返回一个Square*,即使超类返回一个Polygon*,因为它是协变的。一些旧的编译器可能不支持这个,在这种情况下返回一个Polygon*.
class Polygon {
public:
virtual ~Polygon() = 0;
virtual Polygon* clone() const = 0;
virtual double area() { return 0; }
};
inline Polygon::~Polygon() {}
class Square : public Polygon {
public:
Square(double edge) : edge_(edge) {}
virtual Square* clone() const { return new Square(*this); }
virtual double area() { return edge_*edge_; }
private:
double edge_;
};
class Simulation {
public:
Simulation(Polygon const& polygon)
: polygon_(polygon.clone())
{}
Simulation(Simulation const& rhs)
: polygon_(rhs.polygon_->clone())
{}
Simulation& operator=(Simulation const& rhs)
{
if (this != &rhs) {
delete polygon_;
polygon_ = rhs.polygon_->clone();
}
return *this;
}
~Simulation() {
delete polygon_;
}
Polygon* polygon() { return polygon_; }
private:
Polygon* polygon_;
};
如果Simulation
包含Polygon
,那么这意味着它意味着要做一些事情。如果你需要直接从"外部"访问多边形,你要么错过了设计的某个地方,或者如果没有,你可以使用观察者模式,并让多边形通知模拟,如果它的某些变化。
:
outside -> polygon -> callback -> simulation
或
outside -> simulation -> polygon
所以你想要确保外部代码无法改变模拟的内部多边形,但却允许在其中使用任何子类?即确保在c'tor中ref传递的对象的模拟之外没有引用?
你可以想到一个抽象的复制方法来完成:(不要忘记在模拟析构函数中添加delete
)
class Polygon {
public:
virtual Polygon *copy() = 0;
//..
};
class Square : public Polygon {
public:
virtual Polygon *copy() { return new Square(_edge); }
//...
}
class Simulation {
public:
Simulation(const Polygon &p) : poly(p.copy()) {}
};
如果你想复制一个多态对象,这可以用clone方法来完成。
class Polygon
{
...
virtual Polygon* clone() const = 0;
};
class Square: public Polygon
{
...
virtual Square* clone() const { return new Square(*this); }
};
然而,在这个例子中,模拟既没有对多边形本身做任何事情,也不想把它交给其他代码使用,这似乎有点毫无意义。
这就是c++的工作原理。如果为对象编写包装器(PIMPL),则必须实现其完整的接口。这些函数非常小,只是将调用传递给实际实现,但你必须编写它们。然后你可以改变行为,添加日志记录,或任何你需要的…
你只需要决定这个多边形是在模拟的内部还是外部。如果它应该在它的外部,那么你有引用构造函数参数。如果它在内部,则需要以下代码:
class Simulation {
public:
Simulation() : poly(2.0) { }
Polygon *polygon() { return &poly; }
private:
Square poly;
};
现在,多态性方面可以很容易地这样做:
class Simulation {
public:
Simulation() : poly(2.0), poly2(3.0) { }
Polygon *polygon(int i)
{
switch(i) {
case 0: return &poly;
case 1: return &poly2;
}
return 0;
}
private:
Square poly;
Cylinder poly2;
};
一旦你厌倦了添加新的数据成员,这里有另一个技巧可以解决某些情况:
class Simulation {
public:
Simulation() : poly(2.0) { }
Polygon *polygon(float x)
{
poly.edge_ = x;
return &poly;
}
private:
Square poly;
};
编辑:注意头文件中类的顺序需要仔细考虑
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 运行同一解决方案的另一个项目的项目
- 挂起和取消挂起一个文件DLL
- 用C++中的一个变量定义一个常量
- 函数向量_指针有不同的原型,我可以构建一个吗
- 在c++中用vector填充一个简单的动态数组
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- 预处理器:插入结构名称中的前一个行号
- 我在c++代码中生成了一个运行时#3异常
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 从链接列表c++中删除一个项目
- 告诉一个 const char 数组,除了编译时 C 样式的字符串外,它不以 '