3

首先,为了解决我的顾虑,看一下我准备的这两个代码段:

struct Quaternion
{
public:

    float X, Y, Z, W;

    Quaternion(float x, float y, float z, float w)
        :
        X(x), Y(y), Z(z), W(w)
    {}

    void Normalise()
    {
        float num = (((this->X * this->X) + (this->Y * this->Y)) +
            (this->Z * this->Z)) + (this->W * this->W);
        float num2 = 1.0f / (static_cast<float>(std::sqrt(static_cast<double>(num))));
        this->X *= num2;
        this->Y *= num2;
        this->Z *= num2;
        this->W *= num2;
    }

    void Conjugate()
    {
        this->X = -this->X;
        this->Y = -this->Y;
        this->Z = -this->Z;
    }
};

以上是我在标题中提到的类中的“本地方法”。现在让我们看看我所说的类中的“静态方法”是什么意思。

struct Quaternion
{
public:

    float X, Y, Z, W;

    Quaternion(float x, float y, float z, float w)
        :
        X(x), Y(y), Z(z), W(w)
    {}

    static Quaternion& Normalise(Quaternion& quat)
    {
        float num = (((quat.X * quat.X) + (quat.Y * quat.Y)) +
            (quat.Z * quat.Z)) + (quat.W * quat.W);
        float num2 = 1.0f / (static_cast<float>(std::sqrt(static_cast<double>(num))));
        // Assuming operator= overloaded..
        quat = Quaternion(quat.X * num2, quat.Y * num2, quat.Z * num2, quat.W * num2);
        return quat;
    }

    static Quaternion& Conjugate(Quaternion& quat)
    {
        // Assuming operator= overloaded..
        quat = Quaternion(-quat.X, -quat.Y, -quat.Z, quat.W);
        return quat;
    }
};

我的问题是.. 权衡是什么?效果?要使用这些静态类方法而不是本地方法。两者都有相似的用法:

编辑:忽略 *.ToString 功能,它是伪代码 - 我相信你可以想象它会做什么;因此它的实现是多余的,因为它只是打印出原始的 X、Y、Z、W 值。

“本地方法”类用法:

int main()
{
    Quaternion testQuat(6.0f, 6.0f, 6.0f, 1.3f);

    std::cout << testQuat.ToString(); // (6, 6, 6, 1.3)

    testQuat.Conjugate();

    std::cout << testQuat.ToString(); // (-6, -6, -6, 1.3)

    return 0;
}

现在“静态方法”类的用法:

int main()
{
    Quaternion testQuat(6.0f, 6.0f, 6.0f, 1.3f);

    std::cout << testQuat.ToString(); // (6, 6, 6, 1.3)

    testQuat = Quaternion::Conjugate(testQuat);

    std::cout << testQuat.ToString(); // (-6, -6, -6, 1.3)

    return 0;
}

So what is the difference? These are static methods not objects. Which is preferable? Is it just a matter of design choice?

4

3 回答 3

3

They are two totally different things. One of them modifies the object in place a la OOP, the other returns a different object a la functional style. If it was my choice, I would keep both of them as there are use cases for both of them. And I would implement the functional styles as free functions based on the member functions, i.e.:

Quaternion normalize( Quaternion quat )
{
    quat.normalize();
    return quat;
}

[I'm explicitly taking quat by value here, gives a chance for copy-elision]

Note that your static implementations are wrong, they are returning a reference to a temporary. That's undefined behavior, you should get a warning from your compiler and if you are lucky enough a runtime crash as well.

于 2012-12-28T19:29:05.453 回答
1

First off, your second approach shouldn't compile although I think MSVC++ has an error allowing temporary objects to be bound to non-const references. Even adding const& doesn't make the functions better: They still don't work because the caller gets hold of a reference to a temporary. So much for the implementation.

With respect to interface design, I think the real trade-off isn't between static members (you can have them additionally, if you want to) but whether the functions taking no parameters should mutate the object themselves or should return a correspondingly modified object:

// return a copy:
Quaternion Quaternion::conjugate() const {
    return Quaternion(-this->X, -this->Y, -this->Z, this->W);
}

// modify the object itself:
void Quaternion::conjugate() {
    this->X = -this->X;
    this->Y = -this->Y;
    this->Z = -this->Z;
}

Although these two overload actually can live in the same class I would not provide both of them! It is a choice of interface which one is preferable. I would personally prefer the latter and probably create a static member mutating the object itself:

/* static */ void Quaternion::conjugate(Quaternion& object) {
    object = object.conjugate();
}
于 2012-12-28T19:38:27.023 回答
0

Besides the other answers about differences between the two approaches, the static methods are difficult to mock/stub if you want to employ in unit tests.

For example, suppose that you have a class named ClassThatUsesQuaternion that uses Quaternion. If Quaternion have a lot of static methods you will always have real data. On the other hand, if you transform Quaternion methods into virtual methods, you will be able to redefining all methods, creating a environment test under your control. You can even add a mock framework like gmock to put your expectations.

于 2012-12-28T19:43:31.713 回答