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)将演员留在那里可以吗?
答案 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变量(!)暴露给外部代码。