【问题标题】:How to use a member variable as a default argument in C++?如何在 C++ 中使用成员变量作为默认参数?
【发布时间】:2021-07-05 14:37:00
【问题描述】:

我想为其中一个成员函数设置一个可选参数。当没有提供参数时,它将使用成员变量。

但是,当我尝试编译它时,它显示“错误:无效使用非静态数据成员'Object::initPos'

为了隔离问题,我尝试默认一个 int 类型,它编译得很好。 我想知道我的代码有什么问题,以及如何使用成员函数作为默认值。

感谢您的帮助!

对象.h

class Object
{
    public:
       ...
       void MoveTo(double speed, Point position);

    protected:
       Point initPos; 
       Point currPos;

};

对象.c

void Object::MoveTo(double speed, Point position = initPos)
{
    currPos = postion;
}

点.h

class Point
{
    ...

    private:
       double x;
       double y;
       double z; 
};

【问题讨论】:

标签: c++ object default arguments


【解决方案1】:

除了重载之外,另一种方法是将无效值作为默认值传递。然后在你的函数中检查它。

void Object::MoveTo(double speed, Point position = InvalidPosition) {
   position = (position != InvalidPosition) ? position : initPos;
   ...
}

【讨论】:

    【解决方案2】:

    成员函数的默认参数表达式只能依赖于类或全局范围内的事物。默认参数也必须在方法的声明中指定(即在头文件中)。

    要解决这个问题,您需要对 MoveTo 方法进行 2 次重载。一个需要 1 个参数,另一个需要 2 个参数。采用 1 个参数的方法调用另一个方法,并传递您认为是默认值的值。

    void Object::MoveTo(double speed)
    {
        MoveTo(speed, initPos);
    }
    
    void Object::MoveTo(double speed, Point position)
    {
        // Everything is done here.
    }
    

    注意,当你让MoveTo(double)调用MoveTo(double, Point)时,它只允许你编写一次MoveTo的实现,从而尊重DRY的原则。

    【讨论】:

    • 谢谢!我知道我可以这样做。我只是想知道是否有更短的方法可以做到这一点,例如将其设为默认参数。但我想这是唯一的方法。
    • 那么,如果我有很多参数而不是一个可选参数,该怎么办?说 void Object::MoveTo(double speed, Point position = initPos, Point a = m_a, Point b = m_b, Point c = m_c) 那我不用做很多功能吗???
    • @MatthewChan:不幸的是,是的。除了传递许多参数,也许您可​​以更改设计以便用户传递一个对象。该对象已经包含合理的默认值,用户只更改需要不同的默认值。如果您决定这样做,您可能会对 Method Chaining 习惯用法感兴趣,以便更简洁地设置多个参数 (en.wikipedia.org/wiki/Method_chaining)。
    • 只是一个关于方法链接的问题...如果在 C++ 中,那么你会“返回 *this”对吗?那么这不是简单地制作对象实例的副本吗?那么那个实例实际上不会改变吗?就像我有一个名为 Car 的对象,并且我有一个名为 myCar 的 Car 实例。 myCar.setColour(RED).setBrand(PORCHE).setYear(2012)
    • *this 是对当前实例的reference。如果您的 setter 签名类似于Car& setColor(Color c)(注意与号),那么它将返回对汽车的引用,而不是副本。另一方面,如果签名是Car setColor(Color c)(请注意缺少与号),则将返回汽车的副本。为了使方法链起作用,您需要返回对该对象的引用。
    【解决方案3】:

    你可以像这样重载你的函数成员:

    void Object::MoveTo(double speed, Point position) {
       ....
    }
    
    void Object::MoveTo(double speed) {
       Point position = this->initPos;
    
       MoveTo(speed, position);
    }
    

    【讨论】:

    • 不幸的是,唯一可以完成工作的解决方法,虽然看起来很丑:(
    • @Argento 它有什么难看的?重载调用其他重载是很常见的。
    • @TedLyngmo 我已经习惯了其他编程语言,比如(JS、Python、Kotlin 等)与它们相比,并且考虑到 C++ 确实已经具有函数默认值的语法,这是一个丑陋的解决方法,而不是像其他语言一样默认支持的东西。不过,这毕竟是个人意见问题。
    • @Argento 您提到的语言是否通过隐式*this(或等效语言)接受默认值?
    • @TedLyngmo 在 Kotlin 和 JS 中是的,他们确实如此,但不确定 python 以前没有大量使用过它的 OOP。所以上面提到的例子会变成MoveTo(speed, position = initPos) { ... }
    【解决方案4】:

    默认值不是原型的一部分,即它们由调用者解析,而不是由函数本身解析。因此,首先,它们必须对调用者可见。其次,他们不能访问类的受保护成员。 (我很确定你甚至不能使用公共成员作为默认值,但我太累了,无法检查。)

    要解决此问题,请按照其他答案中的建议使用链式重载。

    【讨论】:

    • 谢谢。我尝试将 initPos 更改为 public,但正如您所说,它仍然无法正常工作。那么是不是我们只能使用常量作为默认参数(比如不是成员函数,你可以输入的东西)??
    • they're resolved by the caller, not by the function itself :原来如此。
    • 这是一个比接受的答案更好的答案,因为您实际上解释了问题。
    • @Oliver 那么为什么默认允许静态数据成员?它们并不比其他数据成员更可见(因为调用成员函数时对象将存在)。或者如果静态成员是私有的呢?
    猜你喜欢
    • 1970-01-01
    • 2020-11-27
    • 2015-01-15
    • 2011-01-18
    • 2021-12-29
    • 1970-01-01
    • 2019-04-30
    相关资源
    最近更新 更多