一个简单的问题。 所有可以被声明为const的方法都应该被声明为const方法吗?
这包括不返回任何成员变量或返回成员变量的const引用的方法。除了编译器会指出的明显原因之外,有没有其他理由不这样做?
一个简单的问题。 所有可以被声明为const的方法都应该被声明为const方法吗?
这包括不返回任何成员变量或返回成员变量的const引用的方法。除了编译器会指出的明显原因之外,有没有其他理由不这样做?
一个非const
方法不能通过一个指向常量对象的指针进行调用。 因此,如果方法可以是const
,那么不声明它为const将对其使用施加人为限制。
除此之外,将方法声明为const
是一个重要的语义细节,可以让用户感受到从调用它中可以期望得到的影响。
const
方法的语义不允许您修改包含的指针,但它们不会阻止您更改所指向的数据,因此如果类包含指针,则const
方法可以以编译器不会标记的方式修改对象的某些状态。 struct test { int *p; test() : p( new int() ) {} ~test() { delete p; } int* getP() const { return p; } };
是正确的,并允许int main() { test t; *t.getP() = 10; }
更改存储在类中的值。这就是const
性是一级属性的原因,用户需要解决更深层次的问题。 - David Rodríguez - dribeasint const * getP() const { return p; }
将不会返回指针的副本,而是一个指向常量整数的指针,这样调用者就无法修改您的对象。对于引用也是如此,如果您存储了对可变对象的引用,则该对象(语法上)不属于您的对象,因此您可以从常量成员函数返回引用,这些引用引用该对象,从而可能允许用户代码操作您的内部。 - David Rodríguez - dribeas看起来我在这里是一个不和谐的音符:
我应该将可以成为const的任何方法声明为const方法吗?
不,决策应在设计过程中的不同层次上进行。您应标记所有语义上不修改对象的方法为const
。这可能包括一些实际上修改了某些内部细节但不是对象可感知状态的一部分的方法(这些属性应为mutable
),而可能不包括一些根本不改变任何东西的方法。
enum impl { // different implementations of the algorithm
one_implementation,
another_implementation
};
class example {
mutable std::pair<bool, int> cache;
protected:
int a, b;
public:
example( int a, int b ) : cache(), a(a), b(b) {}
virtual ~example() {}
void set( int _a, int _b ) {
cache.first = false; // invalidate previous result
a = _a;
b= _b;
}
int expensive_calculation() const {
if ( !cache.first ) {
cache.second = calculate();
cache.first = true;
}
return cache.second;
}
virtual void change_impl( impl x ) {}
private:
virtual int calculate() const = 0;
};
当前形式下,你不能改变实现方式,即使change_impl
没有修改任何成员属性也不被标记为const
,因为从语义上讲,它确实发生了更改。
另一方面,expensive_calculation()
方法并未从语义上修改对象的状态,调用操作前后可感知的状态将保持不变,但是它确实修改了一个cache
属性,以加快以后的调用(如果状态未改变)。因此,该方法是const
的,而缓存是mutable
的。
const
,因此使用mutable
来声明否则不是const
的方法为const
根本不相关。 - Alexander Gesslermutable
成员的情况下,mutable
是方法被声明为const
的结果,而不是反过来。也就是说,没有人会吝啬地写出mutable
,而是由于设计决策使方法保持不变,但需要修改该属性。如果你从class test { std::pair<bool,int> cache; int a,b; ...
开始,操作不能被设置为const
,因为它需要修改cache
。然而,从语义上讲,它并没有修改,所以你将其标记为const
,编译器会报错,然后你将cache
标记为mutable
。 - David Rodríguez - dribeasconst
,而不需要其他类,但从设计角度来看则不然:是的,目前它确实没有修改任何成员,但如果你将其标记为const
,那么以后就会遇到问题。现在,如果你说*在仔细设计后,应该将所有在你的设计中都是常量的东西标记为const
*,那么你是完全正确的。但我见过很多人在编译器报错时删除缓存案例中的const
,所以我认为这很重要。 - David Rodríguez - dribeas是的。根据《Effective C++》的说法,“尽可能使用const”。
如果一个方法不修改对象的逻辑状态,那么你应该将这个方法标记为const。这对于你对象的客户端来说是一个很好的服务,因为它们可以使用const引用/指针充分利用对象。
有一种情况,我会三思而后行地使用const:基类的虚函数。继承类不能通过其覆盖函数进行更改可能是一件好事,但某些开发人员可能会持不同意见,因此必须费尽周折。