免责声明:我知道有两个关于const-correctness有用的问题,但是,没有人讨论过如何在C ++ 中使用const-correctness而不是其他编程语言。另外,我我对这些问题的答案不满意。
我现在使用了一些编程语言,而在C ++中让我烦恼的一件事就是const-correctness的概念。在Java,C#,Python,Ruby,Visual Basic等中没有这样的概念,这似乎是C ++非常具体的。
在你推荐我使用C ++ FAQ Lite之前,我已经阅读了它,但这并不能说服我。完全有效,可靠的程序一直用Python编写,没有const关键字或等价物。在Java和C#中,对象可以声明为final(或const),但是没有const成员函数或const函数参数。如果函数不需要修改对象,则它可以采用仅提供对象的读访问权的接口。该技术同样可以在C ++中使用。在我工作的两个真实的C ++系统上,几乎没有使用const,一切都运行良好。因此,对于让const污染代码库的有用性,我还远没有卖掉。
我想知道C ++中是什么让const成为必需,而不是其他编程语言。
到目前为止,我只看到了一个必须使用const 的情况:
#include <iostream>
struct Vector2 {
int X;
int Y;
};
void display(/* const */ Vector2& vect) {
std::cout << vect.X << " " << vect.Y << std::endl;
}
int main() {
display(Vector2());
}
Visual Studio接受使用const注释掉的编译,但警告C4239使用了非标准扩展。所以,如果你想要传递临时代码,避免副本和保持标准兼容的语法简洁,你必须通过const引用,不管它。不过,这更像是一个怪癖,而不是一个根本原因。
否则,实际上不存在必须使用const的情况,除非与使用const的其他代码连接。康斯坦特对我来说似乎不过是一种自以为是的瘟疫,它会传播到它触及的一切:
const在C ++中工作的原因是 因为你可以扔掉它如果你 无法抛弃它,然后是你的世界 会很糟糕如果您声明一个方法 需要一个const Bla,你可以通过 它是一个非常规的Bla。但如果是的话 你的其他方式不能。如果你 声明一个采用a的方法 非const Bla,你不能传递它 const Bla。所以现在你被卡住了。那么你 逐渐需要一个const版本 一切都不是常数,而你 结束了阴影世界。在C ++中你 侥幸逃脱,因为和 C ++中的任何东西都是纯粹可选的 你是否想要这个检查。 你可以打破常量 如果你不喜欢它。
Anders Hejlsberg(C#架构师),CLR Design Choices
答案 0 :(得分:63)
Const正确性为我能想到的C ++提供了两个显着的优点,其中一个使它非常独特。
const_cast
,那么就可变数据与不可变数据的编译器检查安全性。const
,则编译器可以将其放在只读存储器中。这在嵌入式系统中尤为重要。 C ++支持这个;很少有其他语言。这也意味着,在一般情况下,您无法安全地投射const
,尽管在实践中您可以在大多数环境中这样做。 C ++不是唯一具有const正确性的语言或类似的东西。 OCaml和Standard ML有一个类似的概念,使用不同的术语 - 几乎所有数据都是不可变的(const),当你想要某些东西是可变的时,你可以使用不同的类型(ref
类型)来实现它。所以它在相邻语言中只是C ++的独特之处。
最后,走向另一个方向:曾经有一段时间我想在Java中使用const。 final
有时甚至不足以创建明显不可变的数据(尤其是可变数据的不可变视图),并且不想创建接口。查看Java API中的Unmodifiable集合支持,以及它仅在运行时检查是否允许修改const为什么有用的示例(或者至少接口结构应该深入到具有List和MutableList)的事实 - 那里没有理由试图改变不可变结构不能是编译类型错误。
答案 1 :(得分:30)
我认为没有人声称const-correctness是“必要的”。但同样,课程也不是真的有必要,是吗?名称空间,例外情况也是如此......你得到了图片。
Const-correctness有助于在编译时捕获错误,这就是它有用的原因。
答案 2 :(得分:16)
const是一种表达某种东西的方式。如果您认为表达它很重要,那么它在任何语言中都会有用。他们没有这个功能,因为语言设计师没有发现它们有用。如果这个功能在那里,我认为这将是有用的。
我认为它就像Java中的throw规范。如果你喜欢它们,你可能会喜欢其他语言。但是其他语言的设计者并不认为它那么重要。
答案 3 :(得分:13)
你是对的,没有必要保持正确性。您当然可以在没有const关键字的情况下编写所有代码并使其工作正常,就像在Java和Python中一样。
但是如果你这样做,你将不再得到编译器的帮助来检查const违规。编译器在编译时告诉你的错误现在只能在运行时找到,如果有的话,因此需要更长时间来诊断和修复。
因此,试图颠覆或避免使用const-correctness功能只会让你自己从长远来看更加困难。
答案 4 :(得分:11)
嗯,我需要6年才能真正理解,但现在我终于可以回答我自己的问题了。
C ++具有“const-correctness”而Java,C#等没有的原因是C ++仅支持值类型,而这些其他语言仅支持或至少默认为参考类型。
让我们看看C#是一种默认为引用类型的语言,它涉及涉及值类型时的不变性。假设您有一个可变值类型,另一个类型具有该类型的只读字段:
struct Vector {
public int X { get; private set; }
public int Y { get; private set; }
public void Add(int x, int y) {
X += x;
Y += y;
}
}
class Foo {
readonly Vector _v;
public void Add(int x, int y) => _v.Add(x, y);
public override string ToString() => $"{_v.X} {_v.Y}";
}
void Main()
{
var f = new Foo();
f.Add(3, 4);
Console.WriteLine(f);
}
这段代码应该做什么?
答案是#3。 C#尝试通过调用对象的丢弃副本Add方法来尊重“readonly”关键字。这很奇怪,是的,但它有什么其他选择?如果它调用原始Vector上的方法,则对象将发生更改,从而违反字段的“readonly”。如果它无法编译,那么readonly值类型成员就没用了,因为你不能在它们上面调用任何方法,因为它们可能会改变对象。
如果我们只能标记在readonly实例上调用哪些方法是安全的......等等,这正是C ++中的const方法!
C#没有使用const方法,因为我们不使用C#中的值类型;我们只是避免使用可变值类型(并声明它们是“邪恶的”,请参阅1,2)。
此外,引用类型不会遇到此问题,因为当您将引用类型变量标记为只读时,引用的只是引用,而不是对象本身。这对于编译器来说非常容易实施,它可以将任何赋值标记为编译错误,除非在初始化时。如果您使用的只是引用类型,并且您的所有字段和变量都是只读的,那么您在任何地方都会以不小的语法成本获得不变性。 F#完全像这样工作。 Java通过不支持用户定义的值类型来避免此问题。
C ++没有“引用类型”的概念,只有“值类型”(在C#-lingo中);其中一些值类型可以是指针或引用,但与C#中的值类型一样,它们中没有一个拥有它们的存储。如果C ++在其类型上处理“const”,就像C#在值类型上处理“readonly”一样,那就像上面的例子所展示的那样非常混乱,永远不要与复制构造函数进行讨厌的交互。
因此,C ++不会创建一个丢弃的副本,因为这会产生无穷无尽的痛苦。它并不禁止你在成员上调用任何方法,因为,那么语言就不那么有用了。但它仍然希望有一些“只读”或“常识”的概念。
C ++尝试通过标记哪些方法可以安全地调用const成员来找到一种中间方式,然后它相信您在标记中忠实且准确,并直接调用原始对象上的方法。这并不完美 - 它很冗长,你可以随意违反常规 - 但它可以说比所有其他选择更好。
答案 5 :(得分:8)
答案 6 :(得分:5)
如果您正在为具有FLASH或ROM数据的嵌入式设备编写程序,那么没有const-correctness就无法生存。它使您能够控制不同类型内存中数据的正确处理。
答案 7 :(得分:4)
您也希望在方法中使用const,以便利用返回值优化。请参阅Scott Meyers更有效的C ++第20项。
答案 8 :(得分:3)
talk and video 中的Herb Sutter解释了const
关于线程安全的新内涵。
Constness 可能不是你以前需要担心的事情,但是如果你想编写线程安全的代码你需要了解{{1}的重要性,那么使用C ++ 11 }和const
答案 9 :(得分:2)
在C,Java和C#中,您可以通过查看调用站点来判断是否可以通过函数修改传递的对象:
在C ++中,你通常无法说明这一点,因为非const引用调用看起来与pass-by-value相同。拥有const引用允许您设置和实施C约定。
这会对调用函数的任何代码的可读性产生相当大的影响。这可能足以证明语言功能的合理性。
答案 10 :(得分:2)
Anders Hejlsberg(C#架构师):...如果声明一个采用非常量Bla的方法,则不能将它传递给const Bla。所以现在你被卡住了。所以你逐渐需要一个不是const的所有东西的const版本,你最终得到了一个阴影世界。
再次说明:如果你开始在某些方法中使用“const”,你通常会在大多数代码中使用它。但是,代码中const修正的维护(键入,重新编译时缺少某些const等)所花费的时间似乎比修复由于不使用const-correctness而导致的可能(非常罕见)问题的时间要大。因此,在现代语言(如Java,C#,Go等)中缺乏const-correctness支持可能会导致相同代码质量的开发时间略有缩短。
自1999年以来,Java Community Process中存在用于实现const正确性的增强请求票证,但由于上述“const污染”以及兼容性原因而于2005年关闭:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070
虽然C#语言没有const正确性构造,但类似的功能可能会很快出现在.NET Framework的“Microsoft Code Contracts”(库+静态分析工具)中,使用[Pure]和[Immutable]属性:{{3} }
答案 11 :(得分:2)
实际上,不是......不完全是这样。
在其他语言中,尤其是函数或混合语言,如Haskell,D,Rust和Scala,你有可变性的概念:变量可以是可变的,也可以是不可变的,默认情况下通常是不可变的。
这让你(和你的编译器/解释器)更好地理解函数:如果你知道一个函数只接受不可变参数,那么你就知道函数不是那个改变你的变量并导致错误的函数。
C和C ++使用const做类似的事情,除了它是一个不那么坚定的保证:不强制的不变性;调用堆栈中的一个函数可以抛弃constness并改变你的数据,但这会故意违反API契约。因此,意图或最佳实践是使其在其他语言中与不变性一样工作。
所有这一切,C ++ 11现在有一个实际的可变关键字,以及更有限的const关键字。
答案 12 :(得分:1)
C ++中的const关键字(适用于参数和类型声明)试图阻止程序员从大脚趾射击并在整个过程中完成整个过程。
基本思想是将某些内容标记为“无法修改”。无法修改const类型(默认情况下)。 const指针不能指向内存中的新位置。简单,对吧?
嗯,这就是const正确性的来源。以下是使用const时可以找到的一些可能的组合:
一个const变量 意味着不能修改变量名称标记的数据。
指向const变量的指针 意味着可以修改指针,但数据本身不能。
指向变量的const指针 意味着指针不能被修改(指向新的内存位置),但是指针指向的数据可以被修改。
指向const变量的const指针 意味着指针和指向它的数据都不能被修改。
你看到有些事情在那里有些愚蠢吗?这就是为什么当你使用const时,重要的是要正确标记你所在的const。
关键是这只是一个编译时的黑客攻击。标签只是告诉编译器如何解释指令。如果你抛弃const,你可以做任何你想做的事。但是你仍然需要调用具有const要求的方法,并使用适当的类型转换。
答案 13 :(得分:0)
例如,你有一个功能:
void const_print(const char* str)
{
cout << str << endl;
}
另一种方法
void print(char* str)
{
cout << str << endl;
}
主要:
int main(int argc, char **argv)
{
const_print("Hello");
print("Hello"); // syntax error
}
这是因为“hello”是一个const char指针,(C风格)字符串被放在只读内存中。 但是当程序员知道值不会被改变时,它总体上是有用的。因此得到编译器错误而不是分段错误。 就像在非通缉的作业中一样:
const int a;
int b;
if(a=b) {;} //for mistake
因为左操作数是一个const int。