【问题标题】:c++ const member functionc++ const成员函数
【发布时间】:2010-07-02 11:43:58
【问题描述】:

我正在阅读一本名为“Effective C++, Second Edition”的书,其中讨论了 const 成员函数以及如何拥有按位 const 和概念 const。

它说大多数编译器将使用按位 const-ness,即您不能在 const 成员函数中更改对象的数据成员。

然后有一个成员函数的例子,它在 const 测试中似乎没有按位运行。

是这样的:

#include "stdafx.h"
#include <string>
#include <iostream.h>

using namespace std;

class mystring
{

public:
    mystring(const char* value);

    operator char *() const { return data; }

private:
    char * data;
};

mystring::mystring(const char * value)
{

    mystring::data = const_cast<char*>(value);
}


int main(int argc, char* argv[])
{
    const mystring s = "Hello";

    char * nasty = s;

    *nasty = 'M';

    printf("s: %c", s);

    return 0;
}

当它运行时,它在我的书中说它应该允许您更改s 的值,即使它是const。这是因为 char* 数据指向与 const char* 值指向的相同。 *data 在这种情况下不是 const

但是尝试在 MS VC++ 6.0 中运行它,它会在 *nasty = 'M'; 行引发访问冲突

有人可以解释发生了什么吗?我想我错过了什么?

在我看来,因为我们有一个 const mystring s,所以我们应该无法更改它,但是书中所说的似乎很尴尬。

【问题讨论】:

  • 这与问题无关,但请记住,本书的那个版本和 Visual Studio 6 都可以追溯到语言标准化之前的日子,当时很多常见的还没有想到 C++ 习语。如果您想了解当今如何使用 C++,最好使用第三版(我强烈推荐)和现代编译器。如果您想继续使用 Microsoft,可以免费下载 Visual Studio Express。

标签: c++ function constants member


【解决方案1】:

访问冲突是因为您尝试更改字符串文字。您的代码相当于:

char * p = "Hello";
* p = 'M';

这在 C 和 C++ 中都是非法的 - 与 const 成员函数无关。

【讨论】:

  • 所以我猜字符串文字是一个不可变的对象?
  • @Tony:从标准的角度来看,在任何给定的实现中,字符串文字是否是真的不可变对象是未定义的。该标准仅告诉您该对象修改常量的对象是未定义的行为(与通过常量引用访问的非常量对象相比——见我的回答)
  • @Tony 我对此一无所知 - 所有 C 和 C++ 标准都说,试图修改一个会导致未定义的行为。
  • 通常建议使用复制语义。在这种情况下,在构造函数中复制字符串可能是防止此类问题的最佳方法。另一方面,将数据成员更改为 const 并将转换运算符更改为返回 const char* 也可以。要么使事情保持一致。你的构造函数不应该假设它可以抛弃 const。除了 const 的行为之外,这是本示例要学习的另一个教训。
【解决方案2】:

你得到访问冲突只是因为char* 指针指向一个字符串文字。更改字符串文字是未定义的行为(在您的情况下为 AV),它与 const 正确性无关。

【讨论】:

  • @Tony:使用std::stringsstd::string salute = "Hi"; salute = "Good bye";。 C 解决方案是复制和修改副本,使用文字创建一个非常量对象...char text[] = "Hi!"; text[0] = 'B'; text[1] = 'y'; text[2] = 'e'; --注意数组不能更改大小,将text 编辑为“再见”仅因为“嗨!”和“再见”占用相同数量的内存。另请注意,它被定义为char text[] 而不是char *text,数组和指针相同。
【解决方案3】:

您正在做的是未定义的行为。您正在抛弃 const-ness 并尝试修改常量值。如果该示例是从书中照原样复制的,那么这本书是错误的。

使用const_cast&lt;&gt; 合法的做法是将常量从常量指针/引用中丢弃到非常量对象:

int i = 5;
int const & ir = i;
const_cast<int&>(ir) = 7; // valid
int const * ip = &i;
*const_cast<int*>(ip) = 9; // valid

const int c = 11;
int const & cr = c;
const_cast<int&>(cr) = 13; // Undefined behavior

你不能从对一个真正的 const 对象的引用中去掉 const 的原因是编译器可以决定将对象放在只读内存中,在这种情况下,操作可能会以不同的方式失败(杀死应用程序,忽略更改...)

【讨论】:

    【解决方案4】:

    在您的示例中,您没有更改s,而是尝试更改s 的成员变量指向的内存。由于您可以在许多地方放置const,因此您必须小心您实际声明的const

    您的成员函数operator char*() const 不允许更改任何成员变量。试试operator char *() const { data = "something else"; return data; },你的编译器会告诉你不允许修改data。但是,在这种情况下,您只需返回 data 而无需修改。这是允许的。您甚至可以更改data 指向的内存,如operator char *() const { *data = 'M'; return data; }。但是,这在您的上下文中会失败,因为 data 指向一个不允许您更改的字符串文字。

    【讨论】:

    • 那么为什么这是有效的: const char* v = "Hello"; v = "托尼"; ?
    • @Tony: v 是一个指向常量数据的指针,最初指向一个包含"Hello" 的字符串字面量。 v = "Tony" 将其更改为指向不同的字符串文字。它不会修改第一个字符串。
    • 注意const 的放置位置:char * const v = "Hello"; v = "Tony"; 是一个指向非常量数据的常量指针,因此赋值会导致编译器错误。
    猜你喜欢
    • 2016-12-14
    • 2011-06-04
    • 2016-08-17
    • 2011-11-15
    • 2021-09-11
    • 2021-01-08
    • 1970-01-01
    • 2014-02-23
    相关资源
    最近更新 更多