如何正确返回对类成员的引用?

时间:2009-11-29 14:17:01

标签: c++

class Foo {
    protected:
    QPoint& bar() const;

    private:
    QPoint m_bar;
};

QPoint& Foo::bar() const {
    return m_bar;
}

我收到了这个错误:

错误:从'const QPoint'类型的表达式初始化'QPoint&'类型的引用无效

但是,如果我将其更改为:

QPoint& Foo::bar() const {
    return (QPoint&) m_bar;
}

1)我不明白为什么编译器说我的QPoint是const。

2)将演员留在那里可以吗?

5 个答案:

答案 0 :(得分:22)

在类Foo的非const成员函数中,this指针的类型为Foo* const - 也就是说,指针是const,但不是它指向的实例。但是,在const成员函数中,this指针的类型为const Foo* const。这意味着它指向的对象也是不变的。

因此,在您的示例中,当您使用this->m_bar(其中m_bar只是简短形式)时,m_bar是常量对象的成员 - 这就是您的原因不能将其作为非const引用返回。

这从设计POV实际上是有意义的:如果Foo对象是一个常量对象,并且允许您为常量对象调用Foo::bar,那么,如果这将返回一个非常量参考一些你可以操作的内部,你可以改变一个常量对象的状态。

现在你必须看看你的设计并问问自己你是如何到达这一点的,以及你的实际目的是什么。如果m_bar成员实际上不是对象状态的一部分(例如,它仅用于调试目的),那么您可以考虑将其mutable。如果它是对象状态的一部分,那么你必须问自己为什么要对常量对象的某些内部数据返回非const引用。要么使成员函数非常量,要么返回一个const引用,或者重载成员函数:

class Foo {
public:
  const QPoint& bar() const {return m_bar;}
        QPoint& bar()       {return m_bar;}
  // ...
};

答案 1 :(得分:4)

你要做的就是禁止。你不想要返回QPoint&从你的bar函数中打破封装,因为调用者现在可以从QPoint下面修改m_bar。 (幸运的是你将函数声明为const,或者你不会在这里得到错误,你仍然会打破封装)。

在这种情况下你想要的是

const QPoint &Foo::bar() const
{
    return m_bar;
}

根据用户评论进行修改:

恕我直言,通过向setX添加Foo而不是从应该真正是const的访问器函数返回的非const引用中调用非const函数,可以更好地解决这个问题。重载访问器函数以删除const只是为实际问题提供了创可贴,并隐藏了封装受损的事实。将setX添加到Foo可以修复封装问题,并通过返回对私有数据的非const引用来插入正在创建的泄漏接口。

例如,如果您将foo.bar().setX(100);放在应用程序的许多部分中,稍后将QPoint的类型更改为未实现setX的类型,或者只是重命名函数{{ 1}}说setX你有问题b / c你现在必须绕过重命名/重构所有这些调用。在Foo上创建setX使代码更容易调用setPointX vs foo.setX(100),const是正确的,并封装了您的数据。如果您将QPoint更改为Foo中的2 x和y坐标,那么您的课程之外的任何内容都不得更改b / c您有良好的封装。

答案 2 :(得分:3)

函数上的const告诉编译器该函数不会修改任何成员变量。但是,由于您返回对成员变量的引用,因此无法再保证编译错误。

编译器不期望你的成员变量是const,而是从函数返回const。从函数def。

中删除const

答案 3 :(得分:3)

使用

const QPoint& Foo::bar() const {
    return m_bar;
}

QPoint& Foo::bar() {
    return m_bar;
}

我猜你也可以将m_bar声明为:

mutable QPoint m_bar;

编辑:可变防御

@tstenner:
mutable有它的位置。它不是'邪恶的',绝不仅仅是无效*或铸造。假设如下:

class foo {
    SomeType bar;

public:
    foo() : bar() { }

    const SomeType& bar() const { return a; }
};

在这种情况下,即使从不调用bar(),也总是构造bar。这可能没问题,但是如果SomeType有一个代价高昂的构造函数,那么最好允许延迟实例化bar。

考虑:

class foo2 {
    mutable SomeType* bar;
    mutable bool cached;

public:
    foo2() : bar(0), cached(false) { }

    const SomeType& bar() const {
        if( ! cached ) {
            cached = true;
            bar = new SomeType();
        }
        return *bar;
    }
};

这允许foo2模仿非惰性实例化类,同时被懒惰地实例化。当然,foo2的可变性对线程安全有影响,但如果需要,可以通过锁定来解决这些问题。

答案 4 :(得分:2)

如果它允许您返回对变量的引用,那么您可以修改const类的Foo实例。考虑这样的代码:

void baz(const Foo &foo)
{
  QPoint &ref = foo.bar();  //if it was allowed...
  ref = 5; // ...you could have modified a const object foo!
}

因此,编译器会阻止您这样做。

您应该声明方法返回const QPoint&或修改您对const的真正含义的理解。

有人可能会建议您使用mutable。别。 mutable关键字允许实现类来修改const对象的内部成员变量(例如,用于memoization目的),而不是将非const变量(!)暴露给外部代码。