【问题标题】:Complex arguments in C++ functions - Pointers, refenreces or...?C++ 函数中的复杂参数 - 指针、引用或...?
【发布时间】:2012-07-02 11:41:18
【问题描述】:

我有代码:

class Vector3D : public Vector{
protected:
    Point3D * start;
    Point3D * end;

public:
    ~Vector3D(){delete start; delete end;}
    Vector3D(Point3D * start, Point3D * endOrLength, bool fromLength){
        this->start = start;
        if(fromLength){
            this->end = new Vector3D(*start+*endOrLength); //the operator '+' is defined but I won't put it here,
            //because it's not important now
        }else
            this->end = endOrLength;
    }

    Point3D * getStart(){return start;}
    Point3D * getEnd(){return end;}
};

现在,我有代码了:

Vector3D v(new Point3D(1,2,3), new Point3D(2,3,4), 0); //FIRST CREATION
Vector3D v(new Point3D(1,2,3), new Point3D(1,1,1), 1); //SECOND CREATION

第一次和第二次创建给了我相同的 Vector3D,但我认为它可能会产生内存泄漏。

这是真的吗? 以及如何解决它?我想这样做并不优雅:

...
if(fromLength){
    this->end = new Vector3D(*start+*endOrLength);
    delete endOrLength;
}else
...

也许最好把 const Point3D &endOrLenght, 我不知道什么是好的方式? 与 getStart/getEnd 相同 - 是否应该返回指针

Point3D * getStart(){return start;}

或只是变量

Point3D getStart()(return *start)

?

【问题讨论】:

    标签: c++ function pointers reference parameters


    【解决方案1】:

    首先,我不会像 3d 点那样动态分配值对象。只需使用值,这可能会为您节省很多麻烦。其次,如果你有两种方式来构造你的向量,只需提供两个不同的构造函数:

    class Vector3D {
    public:
        Vector3D( const Point3D& s, const Point3D& e )
          : start( s )
          , end( e )
        {
        }
    
        Vector3D( const Point3D& s, const Vector3D& v )
          : start( s )
          , end( s + v )
        {
        }
    }
    private:
        Point3D start;
        Point3D end;
    };
    

    拥有一个根据函数参数执行两种不同操作的函数从调用方甚至难以理解。很难记住最后的 1 或 0 有什么用处。

    亲切的问候, 托斯滕

    【讨论】:

      【解决方案2】:

      您在此处编写代码可能不是处理此问题的最佳方式,而是直接解决您的问题:

      Vector3D(Point3D * start, Point3D * endOrLength, bool fromLength){
          this->start = start;
          if(fromLength){
              this->end = new Vector3D(*start+*endOrLength); // I think you mean to use endOrLength here and not length.
              if (endOrLength)
                  delete endOrLength;
          }else
              this->end = endOrLength;
      }
      

      我认为更好的解决你的问题是使用智能指针,最好的解决方案是看看你是否可以替换指针。

      class Vector3D : public Vector
      {
      protected:
          Point3D _start;
          Point3D _end;
      
      public:
          Vector3D(const Point3D& start, const Point3D& endOrLength, bool fromLength) :
          _start(start),
          _end(fromLength ? Vector3D(start + endOrLength) : endOrLength)
          {
          }
      
          const Point3D& getStart() const { return _start; }
          const Point3D& getEnd() const { return _end; }
      };
      

      【讨论】:

      • 谢谢,这可以解决代码问题,但正如您所说,我的代码不是最好的方法。
      • @PolGraphic 我更新了我的答案,但我不确定你是否可以使用它。
      • 是的,就是这样!谢谢:-)
      【解决方案3】:

      我认为正确的方法是:

      Vector3D(const Point3D& start, const Point3D& endOrLength, bool fromLength)
      

      这样,所有权就没有歧义了。此外,成员可能不应该是指针,否则这可能会导致悬空指针,而是数据成员。

      或者更好的智能指针。

      如果你使用智能指针,你可以从 get 函数中返回一个智能指针。

      【讨论】:

      • 并使用 Point3D getStart() { return start; } 作为回报。或者,如果您有一个较旧的编译器,可以将其设为 Point3D&(或者当您想确保该点不被更改时,甚至将其设为 const Point3D&)
      • 如果一些基本的点可以通过值传递,而不是作为 shared_ptr 传递,那么应用程序将会更好更简单
      【解决方案4】:

      您的设计可能看起来效率较低,因为它的本质(每个元素都是孤立的)往往会违反操作的局部性。 在一起的事物之间进行复制比对稀有事物的取消引用操作要高效得多。

      而且由于您的向量只是 .... 2*3 数字,因此请避免动态内存的所有复杂性,只需使用正常值语义。

      如果您遇到更大尺寸的对象(如 3d 投影矩阵,可能有多达 16 个系数),请考虑仅将动态内存处理到类内部的想法。 在你的情况下,这可以是

      class Vector3d
      {
          struct data
          { Point3d start, end; };
      
          Vector3d() :dp(new data) {}
          Vector3d(const Point3d& a, const Point3d& b) :p(new data({a,b})) {}
      
          Vector3d(const Vector3d& a) :p(new data(*a.p)) {}
          Vector3d(Vector3d&& a) :p(a.p) { a.p=nullptr; }
          Vector3d& operator=(Vector3d a) { delete p; p=a.p; a.p=nullptr; return *this; }
          ~Vector3d() { delete p; }
      
          const Poin3d& start() const { return p->start; }
          const Poin3d& end() const { return p->end; }
      
          //here define the compound assignment arithmetic
          //e.g.:
          Vector3d& operator+=(const Point3d& ofst)
          { p->start += ofst; p->end += ofst; return *this; }
      
          //alternativelly you can define assign in term of arithmetic
          Vector3d& operator-=(const Poinr3d& ofst)
          { *this = *this - ofst; return *this; } //will create a temporary that will be moved
      
      private:
          data* p;
      };
      
      //and here define the point3d, vectopr3d and mixed arithmetic
      //e.g.:
      Vector3d operator+(Vector3d a, const Point3d& s)
      { a += s; return std::move(a); }
      
      //this follow the alternative example
      Vector3d operator-(const Vecotr3d& a, const Point3d& s)
      { return Vector3d(a.start()-s, a.end()-s); }
      

      通过这种方式,动态内存的所有管理以及副本的生成(在需要时)或移动(在可能的情况下)都留在了类中。 其他一切都适用于标准值语义。

      注意:我假设 Poin3d 有 += -= + 和 - 定义 ....

      【讨论】:

      • 这是个好主意。很抱歉,我只能将一个回复标记为“已接受的答案”。幸运的是,我可以投票给有用的答案 :-)
      • 如果使用 c++11,我建议完全避免手动内存管理,而只使用std::unique_ptr。使复制/移动构造函数/析构函数/赋值操作更加简单(并且更容易获得异常安全权利)。唯一的例外是如果空间或运行时间非常关键,但在这种情况下,最好避免基于每个对象的动态内存分配(例如,使用 perallocated data-objects 池代替)。跨度>
      • @Grizzly:不同之处在于 unique_ptr 可以使用“删除器”,因此它需要添加更多内存来存储至少一个多态内部对象的运行时指针。如果没有给出删除器,好的实现可以避免这种情况。关于池化,由于任何时候都可能需要数据并且不再需要数据,因此您必须管理池,让每个数据(或数据所有者)知道属于哪个池,并且您必须跟踪便便内的数据使用或可以重复使用。这可能会在全局范围内占用更多空间,并且 - 事实上 - 最终会导致复制......(如下)
      • @Grizzly: ... new/delete 在针对操作系统操作时已经使用的功能。内存池是一个好主意,但大多数 CRT 实现本身已经拥有它。
      • @EmilioGaravaglia:由于std::unique_ptr 将删除器类型作为模板参数,它通常不会添加指向多态对象的指针,而只是添加删除器类型的实例(通常是没有成员的仿函数,所以1 字节(+可能的填充)并且调用可能会被内联)。考虑到只为大对象动态分配的建议(以及动态内存分配的运行时/空间开销),这通常不应该是一个问题。关于池:如前所述,我仅在运行时/空间限制非常严格时才推荐它(所以在这些情况下......
      猜你喜欢
      • 1970-01-01
      • 2013-02-19
      • 1970-01-01
      • 2011-06-17
      • 2012-12-07
      • 1970-01-01
      • 2017-07-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多