1. 程式人生 > >函式過載之const

函式過載之const

我們知道,如果函式名相同,在相同的作用域內,其引數型別、引數個數,引數順序不同等能構成函式過載。有趣的是如果同時在類中,對於函式名相同的const函式和非const函式能夠構成過載,同時它們被呼叫的時機為:如果定義的物件是常物件,則呼叫的是const成員函式,如果定義的物件是非常物件,則呼叫過載的非const成員函式。例如:
#include <iostream>
using namespace std;
class A
{
public:
       A( int x )
       {
              m = x;
       }
       
       int

 func( void )
       {
            cout << "non-const" << endl;
            m = 1700; //本函式體沒有宣告為const,允許修改成員資料 
            return m;
       }
       
       //函式體限制為const, 
       int func( void )const
       {
             cout << "const" << endl;             
             return m;
       }
private
:
        int m;
};

int main( void )
{
    A b( 2002 );
    b.func( ); //呼叫 非const版本的func() 
    
    const A c( 2002 );
    c.func( ); //呼叫 const版本的func() 

   system( "PAUSE" );
   return 0;

}

另外,應該把不修改相應實參的形參定義為const引用,否則將限制該函式的使用,下面程式碼將產生編譯錯誤:
 string::size_type find_char(string &s, char c)
 {
  while(i!=s.size()&&s[i]!=c) ++i;
  return i;
 }
 int main()
 {
  find_char("Hello World", 'o') //error
  return 0;
 }

        錯誤的原因:雖然字串字面值傳遞給函式可以產生區域性物件,但是這裡形參被宣告為引用,必須引用一個已存在的物件,所以上述程式碼才會導致編譯錯誤。
        僅當形參是引用或指標時,形參是否為const才有過載現象。
class Account 
{  }; 
void lookup(Account &)
{ }
void lookup(const Account &)
{ }       

void lookup3(Account *)
{ }
void lookup3( Account const*)
{ }
void lookup4(Account *) //錯誤,不能過載
{ }
void lookup4( Account *const)//錯誤,不能過載
{ } 

const Account a(0);
Account b;
lookup(a);  //call lookup(const Account &)
lookup(b);  //call lookup(Account &)

注意:不能基於指標本身是否為const來實現函式的過載。例如,
 f(int *);
 f(int *const);
 以上兩個函式無法構成過載。

通常地,不同的過載函式不能具有相同數目的引數和引數型別。函式的返回值型別,雖然屬於成員函式的signature的組成部分,但僅僅返回值型別不同,是不能構成函式過載的,因為這會造成redefinition的錯誤。

但有一個例外,就是使用const關鍵字進行函式過載,是成員函式成為const函式。見下面的程式碼:

// Overloading Based on const

#include <iostream>

#include <string>

using namespace std;

class AClass

{

public:

         AClass():greetingwords("Hello, world!")

         {

         }

         string greeting()                                 // (1)

         {

                   return greetingwords + " - from nonconst version.";

         }

         //const string greeting()                       // (2)

         //{

         //       return greetingwords + " - from right const version.";

         //}

         //string greeting() const                       // (3)

         //{

         //       return greetingwords + " - from right const version.";

         //}

         const string greeting() const             // (4)

         {

                   return greetingwords + " - from const - const version.";

         }

private:

         const string greetingwords;

};

int main(void)

{

         AClass a_class;

         const AClass b_class;

         cout << a_class.greeting() << endl;

         cout << b_class.greeting() << endl;

         return 0;

}

說明:

a.(1)(4)中的greeting的引數數目和型別都是完全一致的(兩個函式都沒有引數),按照通常的說法,這會出現編譯錯誤,但事實上,上面的程式可以正常執行。因此,(1)(4)中的兩個greeting函式,並無redefinition的問題,這是因為(4)中的greeting函式名稱後有一個const關鍵字的緣故;

b.同樣道理,如果將這個(4)定義的greeting註釋掉,使用(3)中的greeting函式的定義,結果也是正確的,因為(3)中的greeting函式名稱後也有一個const關鍵字;

c.(3)(4)不能同時出現,否則會出現redefinition的編譯錯誤。因為它們之間的不同僅是返回值型別不同,一個是string,另一個是const string,而僅僅是返回值型別的不同,編譯器是無法區分兩個過載函式的不同;

d.基於上面的道理,(1)(2)也不能同時出現;

e.結論。如果兩個函式的引數數目和資料型別都是一樣的,只要將其中一個改為const成員函式(即,函式名稱和引數後又const關鍵字),則仍然可以構成有效的函式過載;

f.輸出。上面的程式碼輸出:

Hello, world! – from nonconst version.

Hello, world! – from const – const version.

       a_class是一個nonconst物件,因此選擇了(1)中的greeting定義;b_class是一個const物件,因此選擇了(4)中的greeting定義。