搜尋

首頁  >  問答  >  主體

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有什么用呢?

PHPzPHPz2767 天前753

全部回覆(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(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
  • 取消回覆