1. 程式人生 > >[C++] explicit 關鍵字

[C++] explicit 關鍵字

C++ 只有explicit關鍵字,沒有implicit關鍵字。編譯器允許解析函式的引數時,對引數作隱式轉換。也就是說,編譯器能夠使用帶單個引數的建構函式,將函式引數轉換為正確的型別。

帶有單個形參的建構函式定義了一種由實參型別到類型別的隱式轉換。

下面的類帶有可用於隱式轉換的建構函式:

class Foo
{
public:
    Foo (int foo) : m_foo (foo) { } //帶單個引數的建構函式,可以用於隱式轉換
    int GetFoo () { return m_foo; }
private:
  int m_foo;
};
void DoBar (Foo foo)
{
  int
i = foo.GetFoo (); } int main () { DoBar (42); }

DoBar的形參為Foo物件,現在卻傳了一個int給它,然而,Foo類存在一個帶有 int 型別的建構函式,所以編譯器就用這個建構函式將引數隱式轉換為Foo物件型別。

如果在建構函式前面加explicit關鍵字,就能阻止編譯器使用這個建構函式做隱式轉換,而是必須明確地寫出:DoBar (Foo (42))

由於無意中的隱式轉換會隱藏bug, 這就是建構函式前面加explicit的原因。

#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std; class MyString { public: // 如果要阻止編譯器隱式呼叫,前面必須加 explicit 關鍵字 MyString(size_t size) : m_size(size) { m_str = string(size, 'x'); } MyString(const char* p) : m_str(p) { } void printContent() const { cout << m_str << endl; } private
: int m_size; string m_str; }; void print(const MyString& mystr) { mystr.printContent(); } int _tmain(int argc, _TCHAR* argv[]) { print(3); system("pause"); return 0; }

上面的例子,本意是要寫成print("3"),但是不小心寫成了print(3), 編譯器自行隱式使用建構函式MyString(3), 將臨時物件傳遞給print,輸出“xxx”,但是如果在建構函式MyString(int size)前加“explicit", 寫成print(3)編譯時就會報錯,顯式寫成print(Mystring(3))就不會。

再如:

class Sales_item {
public:
    // book 的預設引數是空的字串
    // 定義了一種隱式轉換
    Sales_item(const std::string &book = ""): isbn(book), units_sold(0), revenue(0.0) { } 
    Sales_item(std::istream &is);  // 定義了一種隱式轉換
    bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; }
    // default constructor needed to initialize members of built-in type
    Sales_item(): units_sold(0), revenue(0.0) { }
private:
    std::string isbn;
    unsigned units_sold;
    double revenue;
};

string null_book = "9-999-99999-9";
// ok: builds a Sales_item with 0 units_sold and revenue from
// and isbn equal to null_book
item.same_isbn(null_book);

由於same_isbn函式要求傳入Sales_item物件的引數,現在傳入的是string,於是編譯器自動使用帶string引數的建構函式隱式轉換,生成一個Sales_item臨時物件,傳給same_isbn函式。程式碼雖然能執行,但也可能是使用者寫錯了。

一個使用了explicit關鍵字的類:

#ifndef REFPTR_H__INCLUDED_
#define REFPTR_H__INCLUDED_
#undef free //this class redifines free

template <class TYPE>
class RefPtr
{
private:
   TYPE *m_pPtr;
   int *m_pCount;
   void free() throw()
   {
      if (--*m_pCount == 0)
      {
         delete m_pCount;
         delete m_pPtr;
      }
   }
   void copy(const RefPtr<TYPE> & p) throw()
   {
      m_pPtr = p.m_pPtr;
      m_pCount = p.m_pCount;
      ++*m_pCount;
   }
public:
   explicit RefPtr(TYPE *p = NULL) throw() : m_pPtr(p), m_pCount(new int(1))
   {
   }
   RefPtr(const RefPtr<TYPE> & obj) throw()
   {
      copy(obj);
   }
   ~RefPtr() throw()
   {
      free();
   }
   RefPtr<TYPE> & operator=(const RefPtr<TYPE> & rhs) throw()
   {
      if (this != &rhs)
      {
         free();
         copy(rhs);
      }
      return *this;
   }
   TYPE & operator*() const throw()
   {
      return *m_pPtr;
   }
   TYPE * operator->() const throw()
   {
      return m_pPtr;
   }
   virtual bool operator<( const RefPtr<TYPE>& rhs ) const
   {
      return m_pPtr<rhs.m_pPtr;
   }
   virtual bool operator>( const RefPtr<TYPE>& rhs ) const
   {
      return m_pPtr>rhs.m_pPtr;
   }
   virtual bool operator<=( const RefPtr<TYPE>& rhs ) const
   {
      return m_pPtr<=rhs.m_pPtr;
   }
   virtual bool operator>=( const RefPtr<TYPE>& rhs ) const
   {
      return m_pPtr>=rhs.m_pPtr;
   }
   virtual bool operator!=( const RefPtr<TYPE>& rhs ) const
   {
      return m_pPtr!=rhs.m_pPtr;
   }
   virtual bool operator==( const RefPtr<TYPE>& rhs ) const
   {
      return m_pPtr==rhs.m_pPtr;
   }
   bool isNull() const throw()
   {
      return m_pPtr == NULL;
   }
   TYPE*  GetRawPtr() const throw()
   {
      return m_pPtr;
   }
};
#endif