Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;设计相关问题_C++_Polymorphism - Fatal编程技术网

C++ C++;设计相关问题

C++ C++;设计相关问题,c++,polymorphism,C++,Polymorphism,下面是问题的情节:假设我有一些对象的抽象类,让我们称之为Object。它的定义将包括二维位置和尺寸。让它也有一些用于渲染的虚拟void Render(后端和后端)const=0方法 现在我专门化了我的继承树,并添加了矩形和椭圆类。我猜它们不会有自己的属性,但它们会有自己的虚拟void Render方法。假设我实现了这些方法,因此RenderforRectangle实际上绘制了一些矩形,椭圆也是如此 现在,我添加了一个名为Plane的对象,它被定义为类平面:public Rectangle,并且有

下面是问题的情节:假设我有一些对象的抽象类,让我们称之为
Object
。它的定义将包括二维位置和尺寸。让它也有一些用于渲染的
虚拟void Render(后端和后端)const=0
方法

现在我专门化了我的继承树,并添加了
矩形
椭圆
类。我猜它们不会有自己的属性,但它们会有自己的
虚拟void Render
方法。假设我实现了这些方法,因此
Render
for
Rectangle
实际上绘制了一些矩形,椭圆也是如此

现在,我添加了一个名为
Plane
的对象,它被定义为
类平面:public Rectangle
,并且有一个私有成员
std::vector Plane\u objects

紧接着,我添加了一个方法,将一些对象添加到我的平面

问题来了。如果我将此方法设计为
void AddObject(Object&Object)
我将面临无法调用虚拟函数的问题,因为我必须执行类似于
平面对象的操作。推回(新对象(Object))
对于矩形,这应该是
push_back(新矩形(对象))
,对于圆,这应该是
new Circle(…)

如果我将此方法实现为
void AddObject(Object*Object)
,它看起来不错,但在其他地方这意味着调用类似
plane.AddObject(new Rectangle(params))这通常是一个混乱,因为不清楚程序的哪个部分应该释放分配的内存

[“当摧毁飞机时?为什么?我们确定对
AddObject
的调用只作为
AddObject(新事物
)执行。]

我想使用第二种方法引起的问题可以通过使用智能指针来解决,但我确信必须有更好的方法


有什么想法吗?

您可以在接口中添加一个
clone
方法来克隆类型层次结构的实例。每个具体实现都会生成该特定类的一个新实例,因此所有类型信息都会保留下来

class Object {
public:
  virtual Object clone() = 0;
};

class Rectangle : public Object {
public:
  virtual Rectangle clone() { /* create and return an identical Rectangle */ }
};

class Ellipse : public Object {
public:
  virtual Ellipse clone() { /* create and return an identical Ellipse */ }
};

class Plane : public Rectangle {
  std::vector<Object*> plane_objects;
public:
  virtual Plane clone() { /* create and return an identical Plane */ }
  void AddObject(const Object& object) {
    plane_objects.push_back(object.clone());
  }
};
类对象{
公众:
虚拟对象克隆()=0;
};
类矩形:公共对象{
公众:
虚拟矩形克隆(){/*创建并返回相同的矩形*/}
};
类椭圆:公共对象{
公众:
虚拟椭圆克隆(){/*创建并返回相同的椭圆*/}
};
类平面:公共矩形{
std::向量平面对象;
公众:
虚拟平面克隆(){/*创建并返回相同的平面*/}
void AddObject(常量对象和对象){
plane_objects.push_back(object.clone());
}
};

这意味着该平面是其所有对象的所有者,因此它也应该在其析构函数中销毁这些对象。如果这些对象仅在内部访问,则可以使用普通指针存储它们-尽管智能指针使处理更简单、更安全。但是,如果它们已发布,则最好使用智能指针存储它们.

使用智能指针,如
boost::shared\u ptr
boost::intrusive\u ptr

为什么您认为有比使用智能指针更好的方法呢?如果您不采取特别的步骤来确保代码中的异常安全,那么在容器中存储原始指针通常会以失败告终

什么时候毁了飞机?为什么?是我们吗 确保对AddObject的调用是正确的 仅作为AddObject(新事物)完成


不可能确定。但是必须有一些东西来管理对象的生命周期。您应该清楚地记录这是什么东西。

您的实际问题似乎是管理对象的生命周期。想到的四种可能性是:

  • 您的容器(即
    平面
    )拥有所有包含对象的所有权,因此
    一旦自身被销毁,就删除它们

  • 您的容器(
    平面
    )不承担所有权,向容器添加对象的人将负责销毁这些对象

  • 对象的生存期是自动管理的

  • 您可以通过向容器提供实际对象的克隆来避免此问题。容器管理对象的副本,调用方管理原始对象

  • 您目前的做法似乎是#4.通过以下方式:

    plane_objects.push_back(new Object(object));
    
    将对象的副本插入容器中。因此,问题似乎消失了。问问自己这是否真的是您想要的,或者上述选择之一是否更合适


    选项#1和#2很容易实现,因为它们定义了一个实现必须遵循的契约。选项#3需要智能指针或其他涉及引用计数的解决方案

    如果您想继续遵循选项4的方法,例如,您可以使用
    clone
    方法扩展
    对象
    类,以便它返回正确类型的对象。这将消除不正确的
    新对象(…)



    p.S.:注意STL容器似乎是如何处理这个问题的:假设您声明了一个
    向量
    。该向量将包含插入其中的对象的副本(我的答案中的选项4)。但是,如果将集合声明为
    vector
    ,则它将包含对原始对象的引用,但不会管理它们的生存期(我的回答中的选项2)。

    首先,您需要了解对象标识和所有权语义:

    每个对象都是真实的身份,还是只是引用了真实的实体?对于前者,您不能使用clone(),如果实例的所有者被保证比它活得更长,则需要使用引用计数(通过boost::shared_ptr)或按引用传递
    class Object
    {
        public:
            virtual Object* clone() const = 0;
            ...
    };
    
    ...
    
    Object* Rectangle::clone() const
    {
        return new Rectangle(*this);  // e.g. use copy c'tor to return a clone
    }