【问题标题】:function overloading class design函数重载类设计
【发布时间】:2012-11-12 01:33:10
【问题描述】:

我的程序有一个 Car 和 CarManager 类,类似于以下内容:

#include <list>

class Car
{
public:
    void Draw() { Draw(m_opacity); }
    void Draw(float opacity)
    {
    }

private:
    float m_opacity;
};

class CarManager
{
public:
    //Draw cars using their m_opacity member
    void DrawCars() 
    { 
        for(auto i = m_cars.begin(); i != m_cars.end(); i++)
            i->Draw();
    }

    //Draw cars using opacity argument
    void DrawCars(float opacity)
    {
        for(auto i = m_cars.begin(); i != m_cars.end(); i++)
            i->Draw(opacity);
    }

private:
    std::list<Car> m_cars;
}

MyApplication::OnRender()
{
    CarManager* pCarManager = GetCarManager();

    //If this condition is met, I want all cars to be drawn with 0.5 opacity.
    if(condition)
        pCarManager->DrawCars(0.5f);

    //Otherwise, draw cars using their m_opacity value.     
    else
        pCarManager->DrawCars();
}

C++ 不允许将非静态成员用作默认参数,因此我重载了绘图函数。如果没有提供参数,将使用类成员调用函数的重载版本。

每辆车都有一个用于渲染的 m_opacity 成员。但是,在某些情况下,我想为我希望所有汽车使用的不透明度指定一个值。在这些情况下,我希望忽略 m_opacity 以支持我提供的值。

在此示例中,CarManager::DrawCars() 中的渲染代码相当小,因此通过对 Car::Draw() 的不同调用重复相同的代码并不是什么大问题。但在我的实际程序中,重复所有相同的代码是不切实际的。

这开始变得混乱了。有没有更好的方法来解决这个问题?

【问题讨论】:

    标签: c++ overloading default-arguments


    【解决方案1】:

    一种简单的方法是使用魔法值:

    #include <list>
    
    class Car
    {
    public:
        static float noOpacity() { return -1; }
    
        void Draw(float opacity)
        {
            if (opacity==noOpacity()) {
                opacity = m_opacity;
            }
            // etc.
        }
    
    private:
        float m_opacity;
    };
    
    class CarManager
    {
    public:
        //Draw cars using optional opacity argument
        void DrawCars(float opacity = Car::noOpacity(); )
        {
            for(auto i = m_cars.begin(); i != m_cars.end(); i++)
                i->Draw(opacity);
        }
    
    private:
        std::list<Car> m_cars;
    }
    

    【讨论】:

    • opacity==noOpacity() 不是一个好主意。使用 ABS(opacity - noOpacity()) &lt; 0.000001 之类的东西...
    • @Deamonpog:你认为在什么情况下会出现问题?
    • 因为它是一个浮点数。但也许它会起作用,因为 -1 可以在浮点表示中完全按原样表示。我们也可以使用“小于 0”之类的东西,因为我们知道不透明度可能在 0 到 100 之间变化。
    • 如果它需要高效,我可能会建议我想到的这个技巧。假设这个数字从 0.00 到 100.00。所以我们知道我们只使用了两个小数点。所以我们可以只使用整数,数字应该从 0 到 10000。(例如 46.52 表示为 4652)但是如果您经常将数字除以 100 以获得 0 到 100 之间的确切值,这可能会导致麻烦。否则它会更好。只是一个建议。 :)
    【解决方案2】:

    有几种方法可以解决这个问题:

    1. 正如另一个答案中所指出的,您可以使用特殊值来指示应使用默认值。如果数据类型支持明显的特殊值,这可能是正确的解决方案,但它不适用于所有类型,并且实际上相对难以维护。我在生产代码中看到了“不可能”值最终成为可能的情况,从而导致未定义的行为(例如,债券的收益率被认为始终为正,但事实证明债券的收益率可能为负)。
    2. 使用optional&lt;T&gt; 基本上将T 与对象是否实际存在的指示捆绑在一起处理一个特殊值。如果选择确实是二进制的(使用传递的不透明度或对象的不透明度),这可以工作:默认将使用 optional&lt;T&gt; 指示不存在可选参数,否则将使用其值。
    3. 一个更具可扩展性的版本是传递一个函数,该函数通过在Car 对象上调用来确定对象的不透明度:std::function&lt;double(Car const&amp;)&gt;。默认是获取Car 不透明度的函数,但也可以是其他函数,包括始终返回一个常量。

    由于第三个选项有点模糊,我将在下面提供一个示例(假设Car 有一个成员函数opacity() 返回Car 的不透明度):

    void Car::Draw(std::function<double(Car const&)> getOpacity
                         = std::mem_fn(&Car::opacity)) {
        opacity = getOpacity(*this);
        // ...
    }
    

    现在可以很容易地传递其他函数对象以某种方式复制不透明度:

    double ConstantOpacity(Car const&, double value) { return value; }
    double ComputeOpacity(Car const& c, double value) { return (c.opacity() + value) / 2; }
    
    Car* car = ...;
    car->Draw(std::bind(&Car::opacity, _1));        // use the car's opacity
    car->Draw(std::bind(&ConstantOpacity, _1, 0.5); // use opacity 0.5
    car->Draw(std::bind(&ComputeOpacity, _1, 0.5);  // average of the car's opacity and 0.5
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-20
      • 2020-12-11
      • 2011-06-03
      • 2017-04-04
      • 2016-10-27
      • 1970-01-01
      相关资源
      最近更新 更多