【问题标题】:What's wrong with const?const有什么问题?
【发布时间】:2011-01-12 14:26:21
【问题描述】:

const 在 C++ 和 C++0x 中的已知不足是什么?

【问题讨论】:

  • 我认为它们很有用,因为使代码更容易理解。
  • 在你得到答案之前需要陈述一个问题,我想
  • 我看到 const 的唯一问题是它的准备程度和 理解程度 因 C++ 程序员的水平而有很大差异,(关于哪个元素 @987654323 存在一些混淆@ 确实适用于复杂的声明,例如)。此外,它是语言的一部分,应该使用——只要已经阅读了 B.Stroustrup 的书 :-)
  • @Vilx 你不觉得“while(x
  • 有趣的是看看这个问题被关闭和重新打开了多少次。

标签: c++ c++11


【解决方案1】:

const 唯一的问题是它被许多开发人员严重低估。它是 C++ 工具箱中最好的工具之一,非常锋利,而且不伤自己。

【讨论】:

  • "但是让那些嘲笑我的人停止笑的原因是,几天后,很明显,我的一些同事拼命寻找的一些很难重现的错误已经消失了。” (来自chat
【解决方案2】:

主要问题是您必须编写它。它应该是默认值,并且应该明确指定所有可变变量或参数。

【讨论】:

  • 对此赞不绝口。即使(或因为)它会破坏大多数程序员的成见。
  • 除了两件事:大多数变量都是为了写入而创建的。默认情况下让它们全部只读会产生更多的工作。此外,C 和 C++ 语言哲学是程序员知道他们在做什么,并且语言不应该阻止你做任何你想做的事情。
  • @Jay “大多数变量都是为了写入而创建的。” – 一次。我很少在第一次赋值后更改变量的值(容器是一种例外)。
  • 您永远不会发现哪一种方法更好。您为读/写创建的一些对象(循环计数器、迭代器、容器、流、大多数字符串)和其他只分配一次的对象——实际上,我想不出任何例子。为什么你的一次性写入不是持续初始化?
  • 哇,当我自己写的时候,我完全错过了你的答案(现在被删除了,也是这么说的)。我删除我的一个原因是我无法想象它是局部变量的合理默认值。但我绝对可以想象按值参数是正常的。因此,除非您输入“可变”关键字,否则可能默认将参数设为 const?
【解决方案3】:

const 的问题在于,许多程序员似乎无法完全理解它,“半常量正确”的项目根本行不通。这是你需要知道的:

  1. Fooconst Foo(或 Foo const
  2. Foo&const Foo&(或 Foo const&
    • references-to-const 绑定到所有类型的东西,而references-to-non-const 则不
  3. Foo*const Foo*(或 Foo const*
    • 指针变量也可以是Foo* constconst Foo* const(或Foo const* const
  4. void Foo::mutator()int Foo::accessor() const
    • 但 const 成员函数内的指针成员仍指向非 const 对象
    • 因此我们可能会意外地从 const 函数返回非 const 数据
  5. iteratorconst_iterator
    • 迭代器变量也可以是const iteratorconst const_iterator

从没有 const 概念的语言迁移到 C++ 非常困难,很多人都看不到重点。

【讨论】:

  • 吹毛求疵:对对象的引用不能是 const,但引用可以引用 const 对象。 (大多数人都知道你的意思,但是指针的区别更重要,我们有 const 指针和指向 const 的指针。)
  • @Phil:对,我把相关部分从英文改成了C++ ;-)
  • 很多程序员不理解,const 不是问题。这是程序员的问题,也是整个西方世界编程教育的严重可悲状态。
  • @Chris:感谢您的关注!
  • @OverTheEdge 我建议你购买 Scott Meyers 的“Effective C++”(第 3 版)。第 3 项用 10 页来讨论该主题。
【解决方案4】:

我在新闻组中经常看到的两个主要问题是

我认为语言可以/应该支持后者。

可能与对协变成员函数实现的支持结合使用,因为两者都需要某种方式来获取 this 指针的类型。

第三个问题是

干杯,

【讨论】:

  • 哪些 Microsoft API 不支持 const? IIRC Windows API 使用 LPCTSTRs,MFC 正确使用 const IIRC 等等。我想不出任何。
  • @Rup:许多 Microsoft API 不使用大量 const。你最后一次看到 const HANDLE 是什么时候?还是 const IDirect3DDevice9*? Microsoft 将 const 用于字符串,仅此而已。
  • @DeadMG:HANDLE 是一个不透明的对象,如果不使用 API 函数,无论如何您都无法对其进行修改。对于 COM 接口,他们使用 [in][out] 等属性。对于大多数其他 C 类型,他们确实使用const
  • 也许它不是纯粹的 Win32,而是 COM 的 BSTR 不能是 const(因为它是 typedef,它不允许将 const 放在类型的正确部分)。
  • +1 表示“需要同时定义方法的 const 和非 const 版本。” !
【解决方案5】:

const 的问题在于程序员使用它的方式不一致

【讨论】:

    【解决方案6】:

    “错误”的一件事是您不能将 T** 转换为 T const * const * 应该允许,因为它并不危险。不允许 T** 转换为 T const ** 是正确的,该转换无效。

    我有时提到 const 实际上是将接口“拆分”为只读方法和写入方法的一种廉价方法。这在 C++ 中可能是不切实际的。但是,在 Java 中拥有 ReadOnly 版本的集合会更实用,因为它们没有 const 并且它们的集合类型更加面向对象。

    const-ness 不会传播:这里的问题是,如果我 pImpl 我的类,编译器不会“检查”constness,即我的接口类可以有一个“const”方法调用一个非 const 方法pImpl 和编译器不会抱怨。那是因为我的 const 方法唯一能保证不做的事情就是将指针更改为指向不同的对象,而我的 pImpl 永远不会改变。它甚至可以是一个 const 指针(不是指向 const 的指针)。

    shared_ptr<T>shared_ptr<const T> 之间缺乏适当的协方差也可能是一个问题,尽管总的来说我认为这不是问题,但开发人员通常 typedef 他们的 shared_ptr 而很少 typedef the shared_ptr to常量。他们有时会传递 const shared_ptr<T> & 并认为他们正在传递一个指向 const T 的共享指针(与 const T* 一样),但实际上它们不是。

    【讨论】:

    • "无法将 T** 转换为 T const * const *" - 这部分是错误的(至少对于我使用的编译器);我记得这在 C 语言中很烦人,但 C++ 做对了。
    • 你可以对 pimpl 类进行 const 检查,没什么大不了的……例如看看 Qts 对 pimpl 类的使用..(他们使用 2 个私有 pimpl 访问器函数(const/nonconst)访问 pimpl 对象,如果您想确保在不使用这些函数的情况下不访问 pimpl 对象(从而绕过 const-check),您可以为 pimpl 对象使用完全不透明的void*,并添加一些 reinterpret_casts访问函数(查看 Q_DECLARE_PRIVATE 的实现)
    • @anatolyg:你是对的。第一段没有描述 const 的“错误”,因为它不是真的。参见 ISO/IEC 14882:2003 的 4.4 [conv.qual] / 4。
    【解决方案7】:

    “问题”?

    如果您不打算修改传递的指针的值(它纯粹用于函数的传递引用输入),请将其标记为const。如果特定变量的值在其初始化后不会改变,则同上。如果函数可以安全地调用 const 类实例,请将其标记为 const。正确注释const 的项目越多,您无意犯错误的可能性就越小,并且编译器在没有完整知识的情况下理论上能够执行的优化越多(例如在仅使用可用的函数原型进行编译时)。

    当您尝试将const 变量转换为非const 变量时,现代版本的 gcc 支持警告。我建议您启用这些警告。

    唯一需要注意的是你标记constconst char * foo()char * foo() const 不同。

    【讨论】:

    • 我不是在问如何使用 const。我已经知道如何使用 const。我一直在寻找 const 的问题
    【解决方案8】:

    下面的大多数答案都说明了诸如“const 的问题是 X 人做 Y”之类的事情。这些不是答案,而是症状const 没有问题。 const几乎没有有任何问题... 那些不能 RTFM 的人有问题

    【讨论】:

      【解决方案9】:

      const 很棒。 const 很重要。 const-正确性是 API 良好的必要条件。

      然而,const 反复出现两个问题。

      • 无法追溯地将变量标记为const。您必须声明一个变量代码,在这种情况下您必须立即对其进行初始化。但是,如果初始化代码包含if 怎么办?您可以选择省略const(不受欢迎),使用运算符? 而不是if(损害可读性)。 Java 是对的,顺便说一句 - const 变量不必立即初始化,它们只需要在第一次读取之前进行初始化,并且必须在 if 的所有 分支。

      • 无法指定通过引用传递给函数的对象在函数调用期间不会改变。 const T& t not 表示t 指向的对象不会改变,只是引用t 不能用来改变它。编译器仍然必须假设它没有看到的任何函数调用 可能 更改对象。在某些情况下,这会阻止很多优化。

      【讨论】:

        【解决方案10】:

        另一个尚未提及的问题是设计糟糕的接口可能会破坏 const(即使没有强制转换)。

        例子:

        class TreeNode {
        public:
            TreeNode& getParent() const { return *parent_; }
            TreeNode& getLeft()   const { return *left_;   }
            TreeNode& getRight()  const { return *right_;  }
        private:
            //TreeNode has a pointer to the Tree to enable navigation of the tree.
            //Assume that other design constraints mean that this must be a pointer 
            //rather than a reference.
            TreeNode* parent_;
            TreeNode* left_;
            TreeNode* right_;
        };
        //This function demonstrates the ability for const to be subverted.
        TreeNode& remove_const(TreeNode const& toRemoveConstFrom) {
            TreeNode& parent(toRemoveConstFrom.getParent());
            TreeNode& leftChild(parent.getLeft());
            TreeNode& rightChild(parent.getRight());
            return &toRemoveConstFrom == &leftChild ? leftChild : rightChild;
        }
        

        const 的不传递性意味着可以有一个接口,其中可以从对对象的 const 引用获得对对象的非常量引用。这是设计界面时要注意的事情。

        【讨论】:

          【解决方案11】:

          一个问题是该语言还允许您将其 const_cast 去掉,这违背了使用 const 的初衷。

          【讨论】:

          • 不再是这个...const 将无用没有丢弃它的能力,因为在现实世界的代码中,您必须处理程序员编写的 API不了解 const 正确性的人(或者回到过去,const 还不是 C++ 的一部分)。
          • @FredOverflow:但如果这些程序员被那些无法再通过 const-casting 解决他们损坏的代码的人追捕并杀死,这可能是一件好事? ;-) 关于古代代码 - 即使短缺的动机是支持预标准代码和/或约定,语言仍然可能存在缺陷。例如,您可能会认为自动转换为 void* 是 C++ 的一个不足:例如需要它。 std::memcpy,但在 C++ 上下文中,不应将函数设计为静默接受任何指针,而不管类型安全。
          • @Dave:为什么开发者放弃了它?大多数演员表都是邪恶的和/或不必要的。如果你不信任程序员,C++ 是错误的语言。
          • @Steve:现在杀死他们会如何解决问题?让该死的终结者彻底完成这项工作,并将非信徒从历史上抹去!
          • 不,尽管const_cast 是合法的,但写入对象创建为const(即使你已经抛弃了变量 上的常量)是不合法的。
          【解决方案12】:

          有一件事是它仍然有可能颠覆它。也就是说,这样做仍然是合法的:

          void foo(const inst& x)
          {
             const_cast<int&> x = 3;
          }
          

          您甚至可以使用 memset 之类的东西来颠覆它,而无需明确的 const_cast

          这是让编译器强制执行const 和允许非const 感知接口的一些灵活性之间的权衡。

          这导致了另一个限制,因为它没有被普遍接受,部分原因是另一个问题,即使用const 是一个全有或全无的命题。如果您开始使用它,则需要在整个代码库中传播它。

          【讨论】:

          • 不,这可能是未定义的行为(如果创建了原始对象const)并且在此函数的上下文中必须假定为。 const_cast 是合法的,但是写入一个创建为 const 的对象,无论你是否抛弃了 const 性,都是非法的。
          • 嗯,是的,但如果你认为是 const 在合同或承诺我不会改变它(即使它之前是非常量),那么它是有可能打破它.
          • 但是你可以在各种语言中以 cmets 的形式做出承诺,然后颠覆它们。 const 然而是一个非注释/内置工具,它强制您默认情况下不违反合同,而在非 const 正确的语言中,您可能会意外更改记录为不变的内容。
          猜你喜欢
          • 1970-01-01
          • 2010-11-11
          • 2011-02-26
          • 1970-01-01
          相关资源
          最近更新 更多