検索

ホームページ  >  に質問  >  本文

c++ - 关于const_cast的几点疑问

来自《c++primer第5版》,我的疑问是粗体:

const_cast

  const_cast只能改变运算对象的底层const

const char *pc; 
char *p=const_cast<char*>(pc);//正确:但通过p写值是未定义的后果

对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉const性质”。一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。
上面这句话是什么意思?是说

char *pc;
const char *p=const_cast<const char*>(pc);

用来对p写值是合法的行为吗?我觉得反过来才对吧?(我已经懂了,作者意思应该是说非常量constcast 成常量后再const cast成非常量的这种情况是可以获得写权限的)

然而如果对象是一个常量,在使用const_cast执行写操作就会产生未定义的后果。
我想const_cast的目的就是用来对const对象写值,如果“通过p写值是未定义的后果”那么使用const_cast有什么用呢?

PHPzPHPz2803日前780

全員に返信(2)返信します

  • 阿神

    阿神2017-04-17 12:07:39

    对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉const性质”。一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。

    上面这句话是什么意思?

    答:即使const_cast可以去掉指针或引用的常量性(去const)得到非const的指针或引用,但使用转换后的这个指针或引用,去修改原本被声明为const的对象,会引发未定义行为。示例

    const int j = 3; // j 声明为const常量
    int* pj = const_cast<int*>(&j);  
    *pj = 4;         // 用去const的指针去修改一个原本就是常量的j的值,undefined behavior!

    如果j声明为int j = 3;它不是一个常量,上面的代码就是合法并预期工作的。

    我想constcast的目的就是用来对const对象写值,如果“通过p写值是未定义的后果”那么使用constcast有什么用呢?

    答:如下用处:

    1. 最常见的,有一些人声明函数类型时,即使在函数里不修改指针的值,也不声明为const,典型的懒,比如
      void log(char* msg);
      明明只是用来把msg打印出来,但它就是不加上const。遇到这种情况,我们只好把手上的const指针转为非const再传给它,因为c++编译器不允许你直接把const指针直接丢给char*,会报错,而c编译比如gcc,只是产生一个warning而已。

    2. 这种我是在effective c++看到的用法,示例代码:

    class MyClass
    {
        char cached_data[10000]; // should be mutable
        bool cache_dirty;        // should also be mutable
    
      public:
    
        char getData(int index) const
        {
            if (cache_dirty)
            {
              MyClass* thisptr = const_cast<MyClass*>(this);
              update_cache(thisptr->cached_data);
            }
            return cached_data[index];
        }
    };

    简言之,就是在一个const成员函数里,要修改没有声明为mutable的成员变量。

    1. 可以去掉volatile修饰的变量,具体作用有待了解。


    上面也说了“只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量都将引发编译器错误”,下面的例子却是

    static_cast<string>(cp);//正确:字符串字面值转换成string类型

    这不是自相矛盾么?

    首先

    只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量都将引发编译器错误

    这句话的意思是说把表达从const转非const,或反过来,只能用const_cast,而其它的形式如

    static_cast<char*>(cp);

    这是不行的。

    然后

    不能用const_cast改变表达式的类型

    这句话是说用const_cast时,要转的类型跟被转的类型,要一致,只是有没有const修饰符的区别,如

    const_cast<string>(cp);

    这是不行的,因为cp的类型是char*,跟string完全不同,如果cp为const string cp那就可行。

    但是
    staticcast<string>(cp) 是可行的,因为它没有改变const,它只是改变了类型,不属于上述constcast说的两种情况。

    其实就是书本的内容浓缩了,然后把代码放一起节省篇幅,导致你误解了。

    返事
    0
  • 大家讲道理

    大家讲道理2017-04-17 12:07:39

    首先, const修饰符加不加是会构成操作符重载的,与const对应的是volatile,统称为cv修饰符。
    我想constcast的目的就是用来对const对象写值,如果“通过p写值是未定义的后果”那么使用constcast有什么用呢?
    const_cast只是编译时的约定,而“通过p写值是未定义的后果”是运行时的问题,这2个约束不是一回事
    上面也说了“只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量都将引发编译器错误”,下面的例子却是
    你的例子是对的,因为你是用static_cast转换的

    #include <stdio.h>
    #include <iostream>
    using namespace std;
    #include <string>
    using std::string;
    
    class MyClass
    {
    public:
        void test()
        {
            cout << "none const object"<<endl;
        }
        void test() const
        {
            cout << "const object" << endl;
        }
    
        void test() volatile
        {
            cout << "volatile object" << endl;
        }
    };
    
    int main()
    {
        MyClass *a = new MyClass();
        const MyClass *b = new MyClass();
        volatile MyClass *c = new MyClass();
        a->test();
        b->test();
        c->test();
        MyClass *d = 0;
        // d = b; 编译会报错,无法去掉const属性,需要
        d = const_cast<MyClass*>(b);
        d->test();
        system("Pause");
        return 0;
    }

    返事
    0
  • キャンセル返事