我想创建一个带有将元素添加到包含抽象父类(super class)的子类的列表的函数的类。
我有一个带有多个子类的抽象类“Shape”。在我的示例中,我使用了“圆形”子类,但是可能存在“矩形”,“多边形”或任何其他形状。
ShapeList类的外观(简化)如下:
ShapeList.h文件
#include "Shape.h"
class Node;
class ShapeList
{
public:
ShapeList() : first(0){};
void add( const Shape& s );
private:
Node *first;
};
ShapeList.cpp文件
#include "ShapeList.h"
class Node
{
public:
friend ShapeList;
Shape *shapes;
Node *next;
Node( Node *n, Shape *s) : next(n), shapes(s){};
};
void ShapeList::add( const Shape& s ){
first = new Node(first, s);
}
super 类形状(也简化了)
class Shape
{
public:
Shape();
Shape(double x, double y) : posX(x), posY(y){};
protected:
virtual const double area() = 0;
double posX;
double posY;
};
子类Circle,它是几个子类之一
Circle.h文件
#include <math.h>
#include <Shape.h>
class Circle : public Shape
{
public:
Circle();
Circle( double x, double y, double r) : Shape(x, y), radie(r){};
const double area();
private:
double radie;
};
Circle.cpp文件
#include "Circle.h"
Circle::Circle()
: Shape(0, 0), radie(0)
{}
const double Circle::area(){
return radie * radie * M_PI;
}
然后,我想做这样的事情:
#include <iostream>
using namespace std;
#include "shapelist.h"
#include "Circle.h"
int main()
{
ShapeList list;
list.add( Circle( 5,5, 3) );
}
所以问题是:
如何将子类作为参数传递给具有父类(super class)参数的函数?可能吗?为每个形状创建添加功能,并且不能选择模板。
我已经测试并在添加功能中进行了很多更改,但是无法正常工作。怎么了?
请注意,我对代码进行了很多次更改,以至于破坏了此处发布的代码中显而易见的内容。你能指出错误吗?
最佳答案
是的,可以将子类作为参数传递给采用父类(super class)参数的函数,但前提是要通过引用或指针传递。如果尝试按值传递,则会遇到object slicing。
但这只是故事的一部分。下一个重要的问题是关于所有权。谁拥有Shape
(以及该问题的Node
)。谁确保删除了它?管理所有权的最简单方法是使用自动存储,然后当它们超出范围时将被自动删除。在这种情况下,您可以考虑main
函数持有的Circle
对象在函数末尾超出范围的可能性。您可以将此Circle
作为Shape
引用传递给ShapeList
,然后ShapeList
不负责删除它。
int main() {
ShapeList list;
Circle c(5, 5, 3);
list.add( c );
}
但是,仔细检查您的代码,似乎您已 promise 拥有
ShapeList
的Shape
。在这种情况下,您必须担心内存管理。我警告您,很难正确地进行手动内存管理(以下可能是我错了)。如果您必须通过指针I来拥有对象的所有权强烈建议像using smart-pointers一样使用 std::unique_ptr
。但是,假设您确实要执行手动内存管理。使用引用传递所有权是非常不寻常的,因此我将
ShapeList::add
更改为采用指针。如果ShapeList
不需要修改Shape
,则它可以是指向const的指针:void ShapeList::add( const Shape *s ){
first = new Node(first, s);
}
然后在主函数中使用
Circle
创建new
:int main() {
ShapeList list;
list.add( new Circle( 10, 10, 4) );
}
现在,
ShapeList
和Node
需要担心手动内存管理,因此您需要考虑rule of three (five)。至少您需要正确定义Node
和ShapeList
的析构函数,并防止复制构造/分配。另外,Shape
必须具有virtual
析构函数才能正确删除它。为了在没有警告的情况下编译代码,我还必须进行一些其他小的更改,但我将留给您查找:
constexpr double PI = 3.141592653589793238463;
class Shape
{
public:
Shape();
Shape(double x, double y) : posX(x), posY(y){};
virtual ~Shape() {} // Important if a pointer-to-shape is being deleted!
virtual double area() const = 0;
protected:
double posX;
double posY;
};
class Node
{
public:
Node *next;
const Shape *shape;
Node( Node *n, const Shape *s) : next(n), shape(s){};
~Node() { delete next; delete shape; }
Node(const Node&) = delete; // Prevent copying to
Node& operator=(const Node&) = delete; // avoid double-deletion.
};
class ShapeList
{
public:
ShapeList() : first(nullptr) {};
~ShapeList() { delete first; }
ShapeList(const ShapeList&) = delete; // Prevent copying to
ShapeList& operator=(const ShapeList&) = delete; // avoid double-deletion.
void add( const Shape* s ){ first = new Node(first, s); }
private:
Node *first;
};
class Circle : public Shape
{
public:
Circle(): Shape(0, 0), radie(0) {}
Circle( double x, double y, double r) : Shape(x, y), radie(r){};
double area() const { return radie * radie * PI; }
private:
double radie;
};
int main() {
ShapeList list;
list.add( new Circle( 10, 10, 4) );
}
如果要保持主函数的外观不变,另一种选择是让
Shape
具有一种多态复制自身的方法,以避免对象 slice 。通常,这是使用 virtual clone
function。然后ShapeList::add
可以通过引用进行引用,然后在内部进行“克隆”操作。当然,实际上,鉴于标准库中已存在
std::list
,因此您无需编写自己的列表类。将其与unique_ptr
is simple放在一起。