iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
自我挑戰組

Effective C++ 讀書筆記系列 第 6

[Day 6] Use const whenever possible (2)

  • 分享至 

  • xImage
  •  

前言

今天就繼續來看const的其他特性。是說本書畢竟是經典老書,有些新語法不在其中,掌握精神之餘應該也要查詢一下是否有現代化手段可以解決。

bitwise constness& logical constness

前面有提到讓整個member function是const,這可以:

  1. 分辨哪些function會動到object
  2. 讓這些function可以與const objects作用

overload

而首先有一點值得注意的是,光是function分別有無const屬性,就可以被overload!例如以下:

class TextBlock{
public:
    const char& operator[](const std::size_t position) const {return text[position];}
    char& operator[](const std::size_t position) const {return text[position];}
}

TextBlock tb("Hello");
std::cout<<tb[0]; // calls non-const operator[]
tb[0] = 'x'; // ok!

void print(const TextBlocl& ctb)
{
    std::cpit<<ctb[0]; // calls const operator[]
    ctb[0] = 'x'; // error!
}

bitwise constness v.s. logical constness

而我們要定義一個member function是const,其實又可以分成bitwise constnesslogical constness兩種。

  • bitwise const: 這也是C++對constness的定義,編譯器會檢查有沒有data members被指派。背後的意義是若且唯若這個function完全不會動到任何non-static的data member,那它就是const?!

但這會有一個問題,有些我們認為不是const的function,它卻會通過bitwise的檢查,例如:

class CTextBlock{
public:
    char& operator[](std::size_t position) const
    { return pTest[position];} // bitwise conost
private:
    char *pTest;
    
const CTestBlock cctb("hello");
char *pc = &cctb[0];
*pc = 'J'; // cctb become "Jello"!
}

照理說我們宣告const,應該不期待它的值會被改變,結果還是可以改它!只是因為pTest指標沒有改指別處,所以它"物理上"是const。

又或者我們心理上覺得應該是const的,它卻會違反bitwise constness:

class CTextBlock{
public:
    std::size_t length() const;
private:
    char *pText;
    std::size_t textLength;

std::size_t CTestBlock::length() const
{
    textLength = std::strlen(pText); // error!
    return textLength;
}

QQ只是算了textLength也不行。但除了這個member,我想讓其他member都有const特性不行?

此時,我們就可以用mutable來解決。

class CTextBlock{
public:
    std::size_t length() const;
private:
    char *pText;
    mutable std::size_t textLength;

std::size_t CTestBlock::length() const
{
    textLength = std::strlen(pText); // error!
    return textLength;
}

std::size_t CTestBlock::length() const
{
    textLength = std::strlen(pText); // ok!
    return textLength;
}

mutable讓我們可以將特定non-static data member指定為可以修改,但仍然可以讓function pass bitwise constness的檢查,讓我們身心靈達到一致!

avoiding duplication in const and non-const

而還有另外一個const衍伸的問題,例如說我們前面overload掉一個是const一個不是const的function,但裡面的內容有一堆重複的,那就會讓程式很冗,例如

class TextBlock{
public:
    const char& operator[](std::size_t position) const
    {
        return text[position];
    }
    
    char& operator[](std::size_t position)
    {
        return text[position];
    }
private:
    std::string text;

此時我們就可以直接call,外加casting來解決,例如

class TextBlock{
public:
    const char& operator[](std::size_t position) const
    {
        return text[position];
    }
    
    char& operator[](std::size_t position)
    {
        return const_cast<char&>(
            static_cast<const TestBlock&>(*this)[position] // call const op[]
            );
    }
private:
    std::string text;

雖然用了兩個casting,但他們是安全的,裡面那個casting是要避免無限呼叫到自己,所以要改成const型態,而外面那個則是要再把return的const型態給remove掉。裡面的非const轉const是安全的,所以直接用static_cast,而要remove掉const型態則只能用const_cast。這看起來不好看,不過可以避免duplication。

要注意的是應該要像這樣讓 非const去call const的才安全,反過來則不然!因為const就是要確保值不會改變,反過來去call可能就會有不預期的變化了。

總結

貼心重點提醒:

  • Declaring something const helps compilers detect usage errors
  • Compilers enforce "bitwise" constness, but you should program using "logical" constness
  • Use non-const version call the const version to solve code duplication when const and non-const member functions have essentially identical implementations

簡單來說就是 1.能用const盡量用,讓compiler幫你卡關 2.compiler只確保物理上的const,但在寫的時候是會用邏輯上的constness來寫,要注意 3. 要避免duplicate的時候可以讓non-const call const版本

總之,作者誠摯推薦const是個好東西,用了絕不後悔!

參考資料


上一篇
[Day 5] Use const whenever possible (1)
下一篇
[Day 7] Make sure that objects are initialized before they're used (1)
系列文
Effective C++ 讀書筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言