我最近与另一位C++开发人员就以下用途进行了交流const:
void Foo(const int bar);
Run Code Online (Sandbox Code Playgroud)
他认为const以这种方式使用是很好的做法.
我认为它对函数的调用者没有任何作用(因为参数的副本将被传递,因此对于覆盖没有额外的安全保证).此外,这样做可以防止实现者Foo修改其参数的私有副本.因此,它既要求又要宣传实施细节.
不是世界末日,但肯定不是值得推荐的良好做法.
我很好奇别人对这个问题的看法.
好吧,我没有意识到参数的常数没有考虑到函数的签名.因此,可以const在实现(.cpp)中标记参数,而不是在标题(.h)中标记- 并且编译器就可以了.既然如此,我想政策应该与制作局部变量const相同.
人们可以提出这样的论点:在标题和源文件中具有不同的看起来签名会使其他人感到困惑(因为它会使我感到困惑).虽然我试着用我写的任何内容来遵循最小惊讶原则,但我认为期望开发人员认为这是合法且有用的是合理的.
我的类中有一个const方法,不能改为非const.在这个方法中,我需要调用一个非const方法,但编译器不允许我这样做.
它有什么办法吗?以下是我的代码的简化示例:
int SomeClass::someMethod() const {
QColor saveColor = color();
setColor(QColor(255,255,255)); // Calling non-const method
// ....
setColor(saveColor); // restore color
return 1;
}
Run Code Online (Sandbox Code Playgroud) 我对本地范围内的任何对象的默认行为是 make it const。例如:
auto const cake = bake_cake(arguments);
Run Code Online (Sandbox Code Playgroud)
我尽量减少非功能性代码,因为这会增加可读性(并为编译器提供一些优化机会)。所以在类型系统中也反映这一点是合乎逻辑的。
然而,使用移动语义,这会产生问题:如果我cake很难或不可能复制并且我想在完成后将其传递出去怎么办?例如:
if (tastes_fine(cake)) {
return serve_dish(cake);
}
Run Code Online (Sandbox Code Playgroud)
据我了解复制cake省略规则,不能保证副本会被删除(但我不确定这一点)。
所以,我不得不搬出cake去:
return serve_dish(std::move(cake)); // this will not work as intended
Run Code Online (Sandbox Code Playgroud)
但这std::move不会有任何用处,因为它(正确地)不会转换Cake const&为Cake&&. 即使对象的生命周期已接近尾声。我们不能从我们承诺不会改变的东西中窃取资源。但这会削弱常量正确性。
那么,我怎样才能拥有我的蛋糕并吃掉它呢?
(即我怎样才能拥有常量正确性并从移动语义中受益。)
我对以下代码中发生的事情感到有点困惑:
const int e = 2;
int* w = ( int* ) &e; // (1) cast to remove const-ness
*w = 5; // (2)
cout << *w << endl; // (3) outputs 5
cout << e << endl; // (4) outputs 2
cout << "w = " << w << endl; // (5) w points to the address of e
cout << "&e = " << &e << endl;
Run Code Online (Sandbox Code Playgroud)
在(1)中,w指向e的地址.在(2)中,该值变为5.但是,当显示*w和e的值时,它们的值不同.但是如果你打印w指针和&e的值,它们具有相同的值/地址.
为什么e仍然包含2,即使它被改为5?他们存放在一个单独的位置?还是暂时的?但是,为什么w指出的价值仍然是e的地址?
D有两种类型的const:不可变变量是声明为不可变的变量,并且总是不可变的,而const变量只是对象的只读版本.
逻辑const是将函数标记为const,但允许对一个或多个成员变量进行写访问.这种方法的典型用途是进行惰性评估,例如(在C++中)
struct Matrix
{
double determinant() const
{
if ( m_dirty )
{
m_determinant = /* expensive calculation */;
m_dirty = false;
}
return m_determinant;
}
void set(int i, int j, double x) { m_dirty = true; ...; }
mutable bool m_dirty;
mutable double m_determinant;
};
Run Code Online (Sandbox Code Playgroud)
在这里,determinant()是const,但仍然可以修改m_dirty,m_determinant因为它们被标记为mutable.
该d常量(FAQ)说,D2不支持,因为它提供了,这是造成障碍编写并发程序,并使得一定的优化更加困难弱势保证逻辑常量.
我完全理解这个问题,但是如果我们需要逻辑const呢?
考虑上面的情况与Matrix类,但没有缓存(和任何需要逻辑const).还想象一下,这个类在我的代码库中使用,并且主要通过const引用访问.
现在考虑分析已经揭示该determinant()函数是代码中的瓶颈,而且它通常被重复访问,其值很少改变,即如上所述的缓存将是完美的优化.
没有逻辑const我怎么能这样做呢?遍及我的代码库将const引用更改为非const引用不是一种选择(出于显而易见的原因). …
在C++中,每次函数不修改对象时,在每次函数"符合"const的情况下,在成员函数定义的末尾添加const是一种很好的做法吗?我知道在这种情况下有必要:
class MyClass {
public:
int getData() const;
};
void function(const MyClass &m) { int a = m.getData(); dosomething... }
Run Code Online (Sandbox Code Playgroud)
但除此之外,以及const对实际功能的其他用途,确实在最后添加const实际上改变了代码的执行方式(更快/更慢),或者它只是编译器处理诸如一个案例的'标志'以上?换句话说,如果类中的功能不需要const(最后),添加它会有什么不同吗?
我目前面临的是由一些高级程序员编写的C++项目,该项目由大约400个文件和200个类组成.
代码精心编写,工作正常,稳定.
虽然我正在添加一些功能,但对我而言,通常的做法是注意 - const正确性.
但是,如果我开始声明我的新成员函数const,那么为了使事情有效,调整旧代码是没有意义的.
const这段代码中引入正确性吗?说我有一个像这样的简单类
class Foo
{
public:
void foo()const
{
str[5] = 'x';
obj->changeTheWorld();
x = 4;
y.get() = 5;
obj2->changeTheWorld();
}
private:
char *str; //some referenced data, not owned by Foo
ComplexObj *obj; //some referenced data, not owned by Foo
int &x; //references as well
//wrapped reference, but has a "T& get()const"
std::reference_wrapper<int> y;
//an occasionally useful pointer wrapper for complex memory cases
//but has a "T* get()const"
std::shared_ptr<ComplexObj> obj2;
};
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为在const方法中,它只是指针本身变为const,而不是它指向的数据.但是在许多情况下,这不是我想要的,如果const方法试图改变这些成员内容(直接或通过在该成员上调用非const方法),我想要编译错误.
有没有标准的解决方案?
我认为某种包装类应该能够实现这一点,并且也应该是编译器优化的东西,尽管还没有坐下来尝试设计这样的东西来覆盖所有的情况下给出一个strong_const<char*> str和strong_const<int&>(也不确定)一个好名字......).
可能重复:
C#中的"const correctness"
我已经编写了很多年的C++,但对C#来说还是比较新的.在学习C#时,我发现const关键字的使用比C++更有限.例如,AFAIK 没有办法向函数const声明参数.我觉得我可能无意中改变我的函数参数(可能是复杂的数据结构),我只能通过测试来检测,这让我感到很不舒服.
你是如何处理这种情况的?
假设我有一个像这样的可调用类型:
struct mutable_callable
{
int my_mutable = 0;
int operator()() { // Not const
return my_mutable++;
}
};
Run Code Online (Sandbox Code Playgroud)
请注意,mutable_callable具有operator()修改成员变量的非常量.....
现在假设我创建了一个std::function我的类型:
std::function<int()> foo = mutable_callable{};
Run Code Online (Sandbox Code Playgroud)
现在我可以这样做:
void invoke(std::function<int()> const& z)
{
z();
}
int main()
{
invoke(foo); // foo changed.....oops
}
Run Code Online (Sandbox Code Playgroud)
现在据我所知std::functionsoperator()是const按照:https :
//en.cppreference.com/w/cpp/utility/functional/function/operator()
所以我的直觉是你不应该这样做......
但随后查看:https : //en.cppreference.com/w/cpp/utility/functional/function/function
这似乎没有对可调用类型是否具有常量施加任何限制operator()......
所以我的问题是这样的:我正确地假设这std::function<int()> const&本质上是一样的,std::function<int()>&因为两者的行为实际上没有区别......如果是这样,为什么不const正确?