【问题标题】:C++: Creating "abstract class" with implemented functionsC++:使用已实现的函数创建“抽象类”
【发布时间】:2021-03-25 13:30:29
【问题描述】:

我创建了一个类来表示向量 (Vec3D),以及它的基本规范操作(求和、标量积、叉积等)。我将使用这个类来派生真实物理单位的类(位置、速度、加速度、力等),它们将具有自己的“专有”特征和操作,*除了它们的矢量操作.

我希望 Vec3D 是不可实例化的,因为一切都必须是一个物理单元,但使其抽象意味着我必须再次为每个派生类重新定义所有运算符。

这里的最佳实践是什么,可以让我利用已经定义的运算符? (也许将构造函数定义为私有,将运算符定义为公共?

在什么情况下需要使用虚函数?

在我的基类之下:

class Vec3D
{
private:
  double _x;
  double _y;
  double _z;

protected:
  // Constructors / Destructor
  Vec3D(const double x, const double y, const double z);
  Vec3D();
  Vec3D(const Vec3D& source);
  Vec3D(Vec3D&& source);
  ~Vec3D(); // Declared just for completeness, although not necessary

public:
  // Operator Overloading
  Vec3D& operator=(const Vec3D& source);
  Vec3D& operator=(Vec3D&& source);
  Vec3D& operator+=(const Vec3D& source);
  Vec3D& operator+=(Vec3D&& source);
  Vec3D operator+(const Vec3D& source) const;
  Vec3D operator+(Vec3D&& source) const;
  Vec3D operator-() const;
  Vec3D& operator-=(const Vec3D& source);
  Vec3D& operator-=(Vec3D&& source);
  Vec3D operator-(const Vec3D& source)const;
  Vec3D operator-(Vec3D&& source) const;
  Vec3D operator*(const double source) const;
  double operator*(const Vec3D& source) const;
  double operator*(Vec3D&& source) const;
  Vec3D operator^(const Vec3D& source) const;
  Vec3D operator^(Vec3D&& source) const;
};

谢谢

【问题讨论】:

  • 您不需要赋值、复制或“移动”函数(以及析构函数)。使用the rule of zero 并依赖编译器生成的隐式运算符和构造函数。此外,对运算符同时使用 const lvalue 和 rvalue 重载是没有意义的。使用其中一种,但不能同时使用。
  • 谢谢,我知道这一点,但不想使用动态成员变量过于复杂。我试图回答的问题是另一个问题。
  • 反之,通过使用非“动态”成员变量,可以使用零规则,不需要Vec3D(const Vec3D& source);Vec3D(Vec3D&& source);~Vec3D();Vec3D& operator=(const Vec3D& source);Vec3D& operator=(Vec3D&& source);。您可以删除所有这些功能,并且复制和移动将仍然按预期工作。
  • 你不需要Vec3D operator-(const Vec3D& source)const;Vec3D operator-(Vec3D&& source) const;。只需要每个函数的左值版本。所有其他功能也是如此。
  • 你的设计不太好用,有几个原因。首先,由于Vec3D 不可实例化,所有返回Vec3D 的方法都是不可调用的,因此完全不需要。其次,所有接受Vec3D 引用的方法都是危险的,因为它们可以用错误的向量类型调用(将速度添加到位置没有意义)。您正在尝试做的是一项相当复杂的任务。我建议在尝试自己制作之前研究处理物理量的现有库,例如 Boost.Units。

标签: c++ polymorphism abstract-class virtual-functions


【解决方案1】:

作为您问题的可能解决方案,您可以做一些类似于我最近为工作做的事情。

我使用Curiously Recurring Template Pattern (CRTP) 作为mixin 的继承组合,然后是赋值运算符的宏。

对于您的情况,您的赋值运算符可以使用普通的受保护成员函数将实际实现卸载到基类。

也许是这样的:

template<typename RealVec>
class VecBase
{
public:
    friend RealVec operator+(RealVec lhs, RealVec const& rhs)
    {
        return lhs += rhs;
    }

    friend RealVec operator*(RealVec lhs, RealVec const& rhs)
    {
        return lhs *= rhs;
    }

protected:
    // Compound addition and assignment
    RealVec& add_assign_this(RealVec& lhs, RealVec const& rhs)
    {
        // To prevent self-assignment
        if (&lhs != &rhs)
        {
            lhs._x += rhs._x;
            lhs._y += rhs._y;
            lhs._z += rhs._z;
        }
        return lhs;
    }

    // Compound multiplication and assignment
    RealVec& mul_assign_this(RealVec& lhs, RealVec const& rhs)
    {
        // To prevent self-assignment
        if (&lhs != &rhs)
        {
            lhs._x *= rhs._x;
            lhs._y *= rhs._y;
            lhs._z *= rhs._z;
        }
        return lhs;
    }

    // Plain assignment, =
    RealVec& assign_this(RealVec& lhs, RealVec const& rhs)
    {
        // To prevent self-assignment
        if (&lhs != &rhs)
        {
            static_cast<VecBase&>(lhs) = rhs;
        }
        return lhs;
    }

    // Protected constructors to inhibit instantiation
    VecBase()
        : _x{ 0 }, _y{ 0 }, _z{ 0 }
    {
    }

    VecBase(int x, int y, int z)
        : _x{ x }, _y{ y }, _z{ z }
    {
    }

    VecBase(VecBase const&) = default;
    VecBase(VecBase&&) = default;

private:
    int _x;
    int _y;
    int _z;

    // The default assignment operator will do the correct thing, no need to implement it
    VecBase& operator=(VecBase const&) = default;
};

class Vector3D : public VecBase<Vector3D>
{
public:
    // Unfortunately we need to reimplement the constructors,
    // but can "call" the base constructors
    Vector3D()
        : VecBase<Vector3D>{ }
    {
    }

    Vector3D(int x, int y, int z)
        : VecBase<Vector3D>{ x, y, z }
    {
    }

    Vector3D(Vector3D const& other)
        : VecBase<Vector3D>(other)
    {
    }


#define DEFINE_COMPOUND_ASSIGNMENT_OPERATOR(oper, func)           \
    Vector3D& operator oper (Vector3D const& rhs)                 \
    {                                                             \
        return VecBase<Vector3D>::func##_assign_this(*this, rhs); \
    }

    DEFINE_COMPOUND_ASSIGNMENT_OPERATOR(+=, add)
    DEFINE_COMPOUND_ASSIGNMENT_OPERATOR(*=, mul)

#undef DEFINE_COMPOUND_ASSIGNMENT_OPERATOR

    // We still need to explicitly define the plain assignment operator
    Vector3D& operator=(Vector3D const& rhs)
    {
        return VecBase<Vector3D>::assign_this(*this, rhs);
    }
};

[See it running on the Compiler Explorer site]

在继承类中写的还是很多的,可惜不能真正避免。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-02
    • 2011-10-10
    • 1970-01-01
    • 2014-07-10
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    相关资源
    最近更新 更多