【问题标题】:Subclass Constructors子类构造函数
【发布时间】:2013-08-04 00:17:12
【问题描述】:

我在使用 C++ 中的子类时遇到问题。我有一个多边形类和一个三角形子类。我希望能够通过传递 Point* 的向量(这是一个自定义类)和 Triangle 通过传递三个 Point*s 来声明多边形。

我的理解是Triangle类的构造函数应该调用Polygon类的构造函数。

这是我目前所拥有的:

class Polygon
{
public:
    vector<Point*> pts;
    Polygon(vector<Point*> aPts) : pts(aPts) {};
};
class Triangle : public Polygon
{
public:
    Triangle(Point* A, Point* B, Point* C) 
    {
        vector<Point*> APts;
        APts.push_back(A); APts.push_back(B); APts.push_back(C);
        Polygon(APts);
    }
};

但是,在 Triangle 构造函数的左括号上,我得到了错误:

error: no matching function call to 'Polygon::Polygon()'

有人可以帮忙吗?

【问题讨论】:

  • 只能从初始化列表中调用父类的构造函数
  • 为什么是指针向量?

标签: c++ inheritance


【解决方案1】:

必须在派生类的构造函数体之前初始化基类;如果构造函数需要参数,则必须在初始化列表中提供这些参数:

Triangle(Point* A, Point* B, Point* C) :
    Polygon({A,B,C})
{}

请注意,这使用 C++11 语法来创建临时向量;如果你被困在过去,那就有点复杂了:

static std::vector<Point*> make_points(Point* A, Point* B, Point* C) {
    std::vector<Point*> points;
    points.push_back(A);
    points.push_back(B);
    points.push_back(C);
}

Triangle(Point* A, Point* B, Point* C) :
    Polygon(make_points(A,B,C))
{}

或者给基类一个默认构造函数可能更容易(但可能更容易出错),然后在派生类构造函数中填充向量:

// In Polygon
Polygon() {} // constructed with no points
// pts must be "public" or "protected"

// In Triangle
Triangle(Point* A, Point* B, Point* C) {
    pts.push_back(A);
    pts.push_back(B);
    pts.push_back(C);
}

【讨论】:

  • 这是对 C++ 初始化列表的巧妙使用。微妙,但聪明;)
  • 这给了我“错误:'{'令牌之前的预期主表达式”。
  • @DanBrenner:那是因为你的编译器卡在过去了;您可能需要告诉它构建 C++11。从错误中,我猜它是 GCC,所以你需要命令行选项 -std=c++11(或 -std=c++0x 对于旧编译器版本)。
  • @iammilind:您的意思是避免创建和移动临时向量?即使成本值得担心,也应该忽略这一举措。
  • @MikeSeymour,你的回答是正确的,我误解了。评论已删除。
【解决方案2】:

您可以在 Polygon 类中使用 addPoint() 函数

class Polygon{
  public:
    vector<Point*> pts;
    Polygon(){}/// < The default constructor
    Polygon(vector<Point*> aPts) : pts(aPts) {}
    void addPoint(Point* p){
        pts.push_back(p);
    }
};
class Triangle : public Polygon{
public:
    Triangle(Point* A, Point* B, Point* C){// calls the default constructor
        addPoint(A);
        addPoint(B);
        addPoint(C);
    }
};

此外,Point 在大多数情况下可以按值复制。我不知道你为什么要通过指针传递它。

【讨论】:

    【解决方案3】:

    它正在尝试调用Polygon 类的默认构造函数,但由于您有自己的构造函数,因此默认的空构造函数不再存在。因此,您必须使用正确的参数调用 Polygon's 构造函数或定义一个空的构造函数。

    另外,Polygon's 构造函数不能带有这样的参数,这必须在 Triangle 类构造函数的初始化列表中完成。这样做是为了确保在创建 derived 类对象之前正确初始化 base 的成员。

    【讨论】:

      【解决方案4】:

      在您的Polygon 类中,您定义了一个带有参数vector 的构造函数。在这种情况下,编译器不会生成默认构造函数,因此您必须Polygon 构造函数传递参数。

      Triangle 中,你定义了一个带有三个参数的构造函数,但你没有调用Polygon 的构造函数。编译器试图找到一个默认构造函数 - 一个没有参数的构造函数 - 但找不到一个,因此会发出错误。

      你不能像你写的那样在Triangle 构造函数的末尾调用Polygon 构造函数。 Polygon ctor 必须在 `Triangle ctor 之前执行。

      尝试将一个 ctor 添加到 Polygon,直接使用三个 Points 而不是 vector,像这样

      class Polygon
      {
      public:
          vector<Point*> pts;
          Polygon(Point* A, Point* B, Point* C) {
              pts.push_back(A);
              pts.push_back(B);
              pts.push_back(C);
          }
      };
      class Triangle : public Polygon
      {
      public:
          Triangle(Point* A, Point* B, Point* C) : Polygon(A, B, C)
          {
          }
      };
      

      【讨论】:

      • 正方形会怎样?
      • square 没有在 OP 中提到:P 这不是一个通用的解决方案,但可以解决所描述的关于调用基类构造函数的问题。应该修复的代码有很多问题,但是代码审查超出了这里的范围。奇怪的是,接受的答案使用 C++11,但问题没有标记为 C++11,因此 OP 在他们的问题中寻找的内容有一些猜测。
      【解决方案5】:

      您必须调用 Polygon 构造函数作为初始化列表的一部分,这意味着您需要构建一个函数来初始化您的向量;

      class Triangle : public Polygon
      {
          // static method, not part of the object being constructed.
          static vector<Point*> BuildVector(Point* A, Point* B, Point* C) {
              vector<Point*> APts;
              APts.push_back(A); APts.push_back(B); APts.push_back(C);
              return APts;
          }
      
      public:
      
          // Need to call Polygon's constructor before even entering our own;
          Triangle(Point* A, Point* B, Point* C) : Polygon(BuildVector(A, B, C))
          {
          }
      };
      

      请注意,通过将指针推入向量而不复制对象,您将面临无法正确处理内存的风险(谁将释放内存?),因此您可能想要使用某种类型的 smart pointer,或者只是将点作为引用传递给构造函数。

      【讨论】:

        【解决方案6】:

        应该从子类的构造函数的初始化列表中调用基类的构造函数。这就是导致代码中的编译器错误的原因。下面的语法也是有效的,但它只是创建了一个 Polygon 的临时对象并将其销毁:

        Polygon(APts);
        

        从设计的角度来看,通过按值复制向量,您正在执行昂贵的操作。
        只是不要这样做。有一个无参数构造函数,然后填充构造函数。

        class Polygon
        {
        public:
            vector<Point*> pts;  // can be made protected as well
            Polygon() {};
        };
        class Triangle : public Polygon
        {
        public:
            Triangle(Point* A, Point* B, Point* C) 
            {
                pts.push_back(A); pts.push_back(B); pts.push_back(C);
            }
        };
        

        如果你支持 C++11,那么同样的代码可以写得更优雅。

        class Polygon {
        protected:
          vector<Point*> pts; 
        public:
          Polygon(std::initializer_list<Point*>& v) : pts(v) {}
        };
        
        class Triangle : public Polygon {
        public:
          Triangle (Point* p1, Point* p2, Point* p3) :
          Polygon({p1, p2, p3}) {}
        };
        
        class Square : public Polygon {
        public:
          Square (Point* p1, Point* p2, Point* p3, Point* p4) :
          Polygon({p1, p2, p3, p4}) {}
        };
        

        用法:

        Triangle t({x, y, z});
        Square s({w, x, y, z});
        

        【讨论】:

        • Triangle s({w, x, y, z}); 呢?
        • @NeelBasu,是的,这是一个很好的观点。我认为在这种情况下我们必须有构造函数。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多