【问题标题】:Question on virtual operator* on C++关于 C++ 上的虚拟运算符*的问题
【发布时间】:2011-11-20 17:26:20
【问题描述】:

我试图在 C++ 上创建虚拟运算符

class Data
{
    virtual Matrix operator* (Matrix &_matrix);
    virtual Scalar operator* (Scalar &_scalar);
};

class Matrix : public Data
{
private:
    vector<vector<double>> data;
public:
    // ...
    Matrix operator* (Matrix &_matrix);
};

class Scalar : public Data
{
private:
    double data;
public:
    // ...
    Scalar operator* (Scalar &_scalar);
};

问题是,当我创建如下所示的 Data* 数组时

Data* arr[10];
arr[0] = new Matrix(3,3);
arr[1] = new Matrix(3,3);

arr[0]->operator*(arr[1]);

我无法在这两个矩阵之间进行乘法运算,因为我无法将 Data 作为参数传递。 但问题是,我不能让函数的参数采用 Data* 类型,因为它无法访问 Matrix 或 Scalar 对象的私有成员。

如何处理这种奇怪的情况?

【问题讨论】:

  • 你只会在运行时知道你是在处理矩阵还是标量?或者你会在编译时知道你的数学表达式吗?
  • 这会很棘手。但请注意,这些运算符的返回类型应为Data*,因为编译器在编译时无法知道结果是Matrix 还是Scalar
  • [ 在仔细思考了这一点之后,我对这种方法有一些评论。 ] 对于我来说,一个好的 API 的基础是显式和隐式语义应该匹配行为。如果使用运算符,则意味着语义必须与内置类型相同。这意味着operator* 是按值返回的。对于协变返回类型,这是不可能的。它遵循你不能有一个具有干净语义的双重调度operator*。我建议要么使用明确的方法名称,要么只定义operator*=

标签: c++ inheritance operator-overloading virtual


【解决方案1】:

类双重调度问题 - 参见 Meyer 的(忘记是哪一个)。 您需要运算符在 lhs 和 rhs 上都是虚拟的,因此您需要两个虚拟调用:

class Matrix;
class Scalar;

class Data
{
public:
    virtual Data* operator* (Data& data) = 0;
    virtual Data* operator* (Matrix &matrix) = 0;
    virtual Data* operator* (Scalar &scalar) = 0;
};

class Matrix : public Data
{
private:
    std::vector<std::vector<double>> data;

    // ...
public:
    Matrix* operator* (Matrix &_matrix)
    {
        // implement
    }
    Matrix* operator* (Scalar& scalar)
    {
        // implement
    }
    Data* operator* (Data &data)
    {
        // Magic here - *this is now Matrix, not Data
        return data * (*this);
    }
};

class Scalar : public Data
{
private:
    double data;
public:
    // ...
    Data* operator* (Data& data)
    {
        // Magic here - *this is now Scalar, not Data
        return data * (*this);
    }
    Scalar* operator* (Scalar &scalar)
    {
        // implement
    }

    Matrix* operator* (Matrix &matrix)
    {
        // Note how we need to allow for parameter reveral during the double dispatch
        Matrix& lhs = matrix;
        Matrix& rhs = *this;
        // Compute matrix product lhs * rhs
    }

};

我已经忽略了返回类型和内存管理的问题。与运算符一样,您最好将*= 定义为原语。然后这可以返回对*this 的引用。然后可以根据*= 定义* 运算符。再一次,这是在“Effective C++”中。

【讨论】:

  • D'oh ...你打败了我一分钟
  • 大体上是正确的,但在具体情况下,您需要一个稍微不同的解决方案,因为矩阵乘法不会交换(即,您不能只反转参数来获得所需的类型信息)。
  • @Ben Jackson。 +1。绝对正确-不敢相信我没有考虑到这一点。但重新审视,我认为这不是什么大问题。它仅适用于 Matrix * Matrix 情况,这需要仔细解释术语才能捕捉到这一点 - 编辑才能做到这一点。
【解决方案2】:

解决方案称为Double Dispatch,这在 C++ 中有点奇怪。下面的代码展示了这个技巧是如何工作的:

#include <iostream>
#include <stdexcept>
#include <vector>


class Matrix;
class Scalar;

class Data {
public:
    virtual ~Data() {}
    virtual Data const& operator* (Data const& other) const = 0;
    virtual Data const& multiplyBy (Data const& other) const {
        throw std::runtime_error("bad function call");
    }
    virtual Data const& multiplyBy (Matrix const& other) const = 0;
    virtual Data const& multiplyBy (Scalar const& other) const = 0;
};

class Matrix : public Data {
private:
    std::vector< std::vector<double> > data;
public:
    virtual Data const& operator* (Data const& other) const {
        return other.multiplyBy(*this);
    }
    virtual Data const& multiplyBy (Matrix const& other) const {
        std::cout << "Matrix * Matrix" << std::endl;
        return *this;
    }
    virtual Data const& multiplyBy (Scalar const& other) const {
        std::cout << "Matrix * Scalar" << std::endl;
        return *this;
    }
};

class Scalar : public Data {
private:
    double data;
public:
    virtual Data const& operator* (Data const& other) const {
        return other.multiplyBy(*this);
    }
    virtual Data const& multiplyBy (Matrix const& other) const {
        std::cout << "Scalar * Matrix" << std::endl;
        return *this;
    }
    virtual Data const& multiplyBy (Scalar const& other) const {
        std::cout << "Scalar * Scalar" << std::endl;
        return *this;
    }
};

int
main() {
    Matrix m;
    Scalar s;
    Data* ary[] = { &m, &s };
    m * s;
    s * m;
    *(ary[0]) * *(ary[1]);
    *(ary[1]) * *(ary[0]);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-12
    • 2012-12-03
    • 2010-10-14
    • 2011-02-16
    • 1970-01-01
    相关资源
    最近更新 更多